From f44fb4630a2dc20fa0c6798ca8195a2b53126e8a Mon Sep 17 00:00:00 2001 From: lenny Date: Tue, 18 May 2021 17:51:22 -0700 Subject: [PATCH] refactor: Remove obsolete V1 code, swagger & scripts closes #3338 & #3337 Signed-off-by: lenny --- Makefile | 1 - bin/edgex-docker-launch.sh | 37 - bin/edgex-launch.sh | 82 - bin/test-go-mod-tidy.sh | 55 - cmd/core-command/main.go | 2 +- cmd/core-data/main.go | 2 +- cmd/core-metadata/main.go | 2 +- cmd/security-bootstrapper/main.go | 3 +- cmd/security-file-token-provider/main.go | 4 +- cmd/security-proxy-setup/main.go | 4 +- cmd/security-secretstore-setup/main.go | 4 +- cmd/support-notifications/main.go | 2 +- cmd/support-scheduler/main.go | 2 +- cmd/sys-mgmt-agent/main.go | 2 +- go.mod | 4 - internal/core/command/README.md | 2 +- internal/core/command/const.go | 25 - internal/core/command/container/device.go | 29 - internal/core/command/device.go | 235 -- internal/core/command/device_test.go | 186 -- internal/core/command/errors/types.go | 61 - internal/core/command/get.go | 60 - internal/core/command/get_test.go | 183 -- internal/core/command/init.go | 43 - internal/core/command/interfaces.go | 22 - internal/core/command/interfaces/db.go | 14 - .../core/command/interfaces/mocks/DBClient.go | 127 - internal/core/command/main.go | 5 +- internal/core/command/put.go | 56 - internal/core/command/put_test.go | 140 - internal/core/command/restDevice_test.go | 432 --- internal/core/command/rest_device.go | 288 -- internal/core/command/router.go | 170 -- internal/core/command/test_utils.go | 18 - internal/core/command/types.go | 59 - internal/core/command/types_test.go | 117 - internal/core/command/utils.go | 43 - internal/core/data/README.md | 2 +- internal/core/data/const.go | 38 - internal/core/data/container/events.go | 30 - internal/core/data/container/metadata.go | 28 - internal/core/data/device.go | 118 - internal/core/data/device_test.go | 114 - internal/core/data/domain_events.go | 60 - internal/core/data/errors/types.go | 172 -- internal/core/data/event.go | 368 --- internal/core/data/event_test.go | 650 ----- internal/core/data/init.go | 25 - internal/core/data/interfaces/db.go | 202 -- .../core/data/interfaces/mocks/DBClient.go | 812 ------ internal/core/data/io.go | 110 - internal/core/data/io_test.go | 117 - internal/core/data/main.go | 5 +- internal/core/data/mocks/device.go | 83 - internal/core/data/operators/reading/db.go | 24 - internal/core/data/operators/reading/get.go | 66 - .../core/data/operators/reading/get_test.go | 164 -- .../data/operators/reading/mocks/Loader.go | 34 - .../data/operators/value_descriptor/db.go | 25 - .../data/operators/value_descriptor/get.go | 93 - .../operators/value_descriptor/get_test.go | 227 -- .../value_descriptor/mocks/Loader.go | 57 - internal/core/data/reading.go | 297 -- internal/core/data/reading_test.go | 295 -- internal/core/data/router.go | 2029 -------------- internal/core/data/router_test.go | 288 -- internal/core/data/utils.go | 46 - internal/core/data/validate.go | 119 - internal/core/data/validate_test.go | 258 -- internal/core/data/valuedescriptor.go | 409 --- internal/core/data/valuedescriptor_test.go | 449 --- internal/core/metadata/README.md | 2 +- internal/core/metadata/container/coredata.go | 29 - .../core/metadata/container/notifications.go | 29 - internal/core/metadata/errors/types.go | 197 -- internal/core/metadata/init.go | 37 - internal/core/metadata/interfaces/db.go | 98 - .../metadata/interfaces/mocks/DBClient.go | 1239 --------- internal/core/metadata/main.go | 5 +- .../metadata/operators/addressable/add.go | 57 - .../operators/addressable/add_test.go | 122 - .../core/metadata/operators/addressable/db.go | 41 - .../metadata/operators/addressable/delete.go | 87 - .../operators/addressable/delete_test.go | 245 -- .../metadata/operators/addressable/get.go | 186 -- .../operators/addressable/get_test.go | 380 --- .../addressable/mocks/AddressDeleter.go | 205 -- .../addressable/mocks/AddressLoader.go | 168 -- .../addressable/mocks/AddressUpdater.go | 226 -- .../addressable/mocks/AddressWriter.go | 32 - .../metadata/operators/addressable/update.go | 104 - .../operators/addressable/update_test.go | 154 -- .../core/metadata/operators/command/db.go | 12 - .../core/metadata/operators/command/get.go | 111 - .../metadata/operators/command/get_test.go | 222 -- .../operators/command/mocks/CommandLoader.go | 101 - .../core/metadata/operators/device/add.go | 96 - .../metadata/operators/device/add_test.go | 194 -- internal/core/metadata/operators/device/db.go | 34 - .../core/metadata/operators/device/events.go | 10 - .../core/metadata/operators/device/get.go | 85 - .../metadata/operators/device/get_test.go | 114 - .../operators/device/mocks/DeviceAdder.go | 116 - .../operators/device/mocks/DeviceLoader.go | 57 - .../device/mocks/DeviceProfileDeleter.go | 136 - .../device/mocks/DeviceProfileUpdater.go | 159 -- .../device/mocks/DeviceServiceLoader.go | 53 - .../operators/device/mocks/DeviceUpdater.go | 151 - .../core/metadata/operators/device/notify.go | 145 - .../metadata/operators/device/notify_test.go | 194 -- .../core/metadata/operators/device/request.go | 72 - .../metadata/operators/device/request_test.go | 51 - .../core/metadata/operators/device/update.go | 199 -- .../metadata/operators/device/update_test.go | 174 -- .../metadata/operators/device_profile/add.go | 63 - .../operators/device_profile/add_test.go | 136 - .../metadata/operators/device_profile/db.go | 68 - .../operators/device_profile/delete.go | 106 - .../operators/device_profile/delete_test.go | 251 -- .../metadata/operators/device_profile/get.go | 194 -- .../operators/device_profile/get_test.go | 381 --- .../mocks/DeviceProfileAdder.go | 32 - .../mocks/DeviceProfileDeleter.go | 251 -- .../mocks/DeviceProfileLoader.go | 168 -- .../mocks/DeviceProfileUpdater.go | 251 -- .../mocks/ValueDescriptorAdder.go | 34 - .../mocks/ValueDescriptorUpdater.go | 106 - .../operators/device_profile/update.go | 77 - .../operators/device_profile/update_test.go | 246 -- .../device_profile/value_descriptor.go | 238 -- .../device_profile/value_descriptor_test.go | 355 --- .../metadata/operators/device_service/db.go | 35 - .../metadata/operators/device_service/get.go | 154 -- .../operators/device_service/get_test.go | 417 --- .../mocks/DeviceServiceLoader.go | 141 - .../mocks/DeviceServiceUpdater.go | 155 -- .../operators/device_service/update.go | 205 -- .../operators/device_service/update_test.go | 301 -- internal/core/metadata/rest_addressable.go | 337 --- .../core/metadata/rest_addressable_test.go | 791 ------ internal/core/metadata/rest_command.go | 126 - internal/core/metadata/rest_command_test.go | 268 -- internal/core/metadata/rest_device.go | 1088 -------- internal/core/metadata/rest_device_test.go | 84 - internal/core/metadata/rest_deviceprofile.go | 613 ---- .../core/metadata/rest_deviceprofile_test.go | 1610 ----------- internal/core/metadata/rest_devicereport.go | 399 --- internal/core/metadata/rest_deviceservice.go | 843 ------ .../core/metadata/rest_deviceservice_test.go | 697 ----- .../core/metadata/rest_provisionwatcher.go | 627 ----- internal/core/metadata/router.go | 1091 -------- internal/core/metadata/utils.go | 27 - internal/pkg/bootstrap/container/database.go | 29 - .../bootstrap/handlers/database/database.go | 163 -- internal/pkg/container/errorHandler.go | 29 - internal/pkg/correlation/models/event.go | 89 - internal/pkg/db/db.go | 38 - internal/pkg/db/interfaces/db.go | 246 -- internal/pkg/db/redis/CONFIGURATION.md | 36 - internal/pkg/db/redis/README.md | 124 - internal/pkg/db/redis/client.go | 28 - .../pkg/db/redis/client_integration_test.go | 110 - internal/pkg/db/redis/data.go | 1312 --------- internal/pkg/db/redis/device.go | 102 - internal/pkg/db/redis/device_profile.go | 72 - internal/pkg/db/redis/device_service.go | 79 - internal/pkg/db/redis/event.go | 117 - internal/pkg/db/redis/metadata.go | 1260 --------- internal/pkg/db/redis/models/db_command.go | 34 - internal/pkg/db/redis/models/interval.go | 62 - .../pkg/db/redis/models/interval_action.go | 72 - internal/pkg/db/redis/notifications.go | 959 ------- internal/pkg/db/redis/object.go | 29 - internal/pkg/db/redis/provision_watcher.go | 88 - internal/pkg/db/redis/queries.go | 328 --- internal/pkg/db/redis/scheduler.go | 428 --- internal/pkg/db/redis/scripts.go | 152 - internal/pkg/db/redis/util.go | 26 - internal/pkg/db/test/db_data.go | 900 ------ internal/pkg/db/test/db_metadata.go | 1154 -------- internal/pkg/db/test/db_notifications.go | 416 --- internal/pkg/db/test/db_scheduler.go | 288 -- internal/pkg/errorconcept/addressable.go | 90 - internal/pkg/errorconcept/cbor.go | 43 - internal/pkg/errorconcept/command.go | 43 - internal/pkg/errorconcept/common.go | 231 -- internal/pkg/errorconcept/const.go | 19 - internal/pkg/errorconcept/db.go | 89 - internal/pkg/errorconcept/default.go | 115 - internal/pkg/errorconcept/device.go | 104 - internal/pkg/errorconcept/device_profile.go | 216 -- internal/pkg/errorconcept/device_report.go | 57 - internal/pkg/errorconcept/device_service.go | 140 - internal/pkg/errorconcept/event.go | 42 - internal/pkg/errorconcept/handler.go | 71 - .../pkg/errorconcept/provision_watcher.go | 194 -- internal/pkg/errorconcept/service_client.go | 39 - .../pkg/errorconcept/value_descriptors.go | 139 - internal/security/bootstrapper/main.go | 3 +- internal/security/fileprovider/main.go | 4 +- internal/security/proxy/main.go | 4 +- internal/security/secretstore/main.go | 4 +- internal/support/notifications/cleanup.go | 76 - internal/support/notifications/const.go | 45 - .../notifications/distribution_coordinator.go | 76 - .../support/notifications/errors/types.go | 59 - .../notifications/escalating_service.go | 62 - internal/support/notifications/init.go | 1 - .../support/notifications/interfaces/db.go | 71 - .../interfaces/mocks/DBClient.go | 789 ------ internal/support/notifications/main.go | 5 +- .../notifications/normal_distribution.go | 40 - .../operators/notification/db.go | 36 - .../operators/notification/delete.go | 96 - .../operators/notification/delete_test.go | 187 -- .../operators/notification/get.go | 225 -- .../operators/notification/get_test.go | 572 ---- .../notification/mocks/NotificationDeleter.go | 52 - .../notification/mocks/NotificationLoader.go | 191 -- .../operators/subscription/add.go | 43 - .../operators/subscription/add_test.go | 87 - .../operators/subscription/db.go | 42 - .../operators/subscription/delete.go | 74 - .../operators/subscription/delete_test.go | 133 - .../operators/subscription/get.go | 117 - .../operators/subscription/get_test.go | 280 -- .../subscription/mocks/SubscriptionDeleter.go | 38 - .../subscription/mocks/SubscriptionLoader.go | 99 - .../subscription/mocks/SubscriptionUpdater.go | 113 - .../subscription/mocks/SubscriptionWriter.go | 32 - .../operators/subscription/update.go | 50 - .../operators/subscription/update_test.go | 103 - .../notifications/rest_notification.go | 505 ---- .../notifications/rest_notification_test.go | 1016 ------- .../notifications/rest_subscription.go | 397 --- .../notifications/rest_subscription_test.go | 567 ---- .../notifications/rest_transmission.go | 394 --- internal/support/notifications/router.go | 413 --- .../support/notifications/sending_service.go | 294 -- .../notifications/sending_service_test.go | 136 - internal/support/notifications/utils.go | 54 - internal/support/scheduler/const.go | 39 - internal/support/scheduler/container/queue.go | 29 - internal/support/scheduler/errors/types.go | 152 - internal/support/scheduler/init.go | 19 +- internal/support/scheduler/interfaces/db.go | 82 - .../scheduler/interfaces/mocks/DBClient.go | 378 --- .../interfaces/mocks/SchedulerQueueClient.go | 200 -- .../scheduler/interfaces/scheduler_queue.go | 58 - internal/support/scheduler/interval.go | 105 - internal/support/scheduler/interval_action.go | 321 --- .../support/scheduler/interval_action_test.go | 197 -- internal/support/scheduler/interval_test.go | 174 -- internal/support/scheduler/loader.go | 322 --- internal/support/scheduler/main.go | 5 +- .../scheduler/operators/interval/add.go | 62 - .../scheduler/operators/interval/add_test.go | 161 -- .../scheduler/operators/interval/db.go | 73 - .../scheduler/operators/interval/delete.go | 158 -- .../operators/interval/delete_test.go | 366 --- .../scheduler/operators/interval/get.go | 103 - .../scheduler/operators/interval/get_test.go | 260 -- .../interval/mocks/IntervalActionLoader.go | 34 - .../interval/mocks/IntervalDeleter.go | 157 -- .../interval/mocks/IntervalLoader.go | 99 - .../interval/mocks/IntervalUpdater.go | 136 - .../interval/mocks/IntervalWriter.go | 120 - .../interval/mocks/SchedulerQueueDeleter.go | 67 - .../interval/mocks/SchedulerQueueLoader.go | 53 - .../interval/mocks/SchedulerQueueUpdater.go | 67 - .../interval/mocks/SchedulerQueueWriter.go | 67 - .../scheduler/operators/interval/update.go | 105 - .../operators/interval/update_test.go | 261 -- .../scheduler/operators/intervalaction/add.go | 96 - .../operators/intervalaction/add_test.go | 207 -- .../scheduler/operators/intervalaction/db.go | 45 - .../scheduler/operators/intervalaction/get.go | 55 - .../operators/intervalaction/get_test.go | 176 -- .../mocks/IntervalActionLoader.go | 187 -- .../mocks/IntervalActionWriter.go | 208 -- .../mocks/SchedulerQueueWriter.go | 67 - internal/support/scheduler/rest_interval.go | 325 --- .../support/scheduler/rest_interval_test.go | 820 ------ .../support/scheduler/rest_intervalaction.go | 419 --- .../scheduler/rest_intervalaction_test.go | 251 -- internal/support/scheduler/router.go | 225 -- internal/support/scheduler/schedule.go | 32 +- internal/support/scheduler/schedulecontext.go | 8 +- .../scheduler/schedulercontext_test.go | 250 -- internal/support/scheduler/utils.go | 15 - internal/system/agent/main.go | 3 +- openapi/v1/core-command.yaml | 376 --- openapi/v1/core-data.yaml | 1061 ------- openapi/v1/core-metadata.yaml | 2460 ----------------- openapi/v1/support-logging.yaml | 485 ---- openapi/v1/support-notifications.yaml | 1325 --------- openapi/v1/support-scheduler.yaml | 445 --- openapi/v1/system-agent.yaml | 149 - 298 files changed, 36 insertions(+), 61501 deletions(-) delete mode 100755 bin/edgex-docker-launch.sh delete mode 100755 bin/edgex-launch.sh delete mode 100755 bin/test-go-mod-tidy.sh delete mode 100644 internal/core/command/const.go delete mode 100644 internal/core/command/container/device.go delete mode 100644 internal/core/command/device.go delete mode 100644 internal/core/command/device_test.go delete mode 100644 internal/core/command/errors/types.go delete mode 100644 internal/core/command/get.go delete mode 100644 internal/core/command/get_test.go delete mode 100644 internal/core/command/interfaces.go delete mode 100644 internal/core/command/interfaces/db.go delete mode 100644 internal/core/command/interfaces/mocks/DBClient.go delete mode 100644 internal/core/command/put.go delete mode 100644 internal/core/command/put_test.go delete mode 100644 internal/core/command/restDevice_test.go delete mode 100644 internal/core/command/rest_device.go delete mode 100644 internal/core/command/router.go delete mode 100644 internal/core/command/test_utils.go delete mode 100644 internal/core/command/types.go delete mode 100644 internal/core/command/types_test.go delete mode 100644 internal/core/command/utils.go delete mode 100644 internal/core/data/const.go delete mode 100644 internal/core/data/container/events.go delete mode 100644 internal/core/data/container/metadata.go delete mode 100644 internal/core/data/device.go delete mode 100644 internal/core/data/device_test.go delete mode 100644 internal/core/data/domain_events.go delete mode 100644 internal/core/data/errors/types.go delete mode 100644 internal/core/data/event.go delete mode 100644 internal/core/data/event_test.go delete mode 100644 internal/core/data/interfaces/db.go delete mode 100644 internal/core/data/interfaces/mocks/DBClient.go delete mode 100644 internal/core/data/io.go delete mode 100644 internal/core/data/io_test.go delete mode 100644 internal/core/data/mocks/device.go delete mode 100644 internal/core/data/operators/reading/db.go delete mode 100644 internal/core/data/operators/reading/get.go delete mode 100644 internal/core/data/operators/reading/get_test.go delete mode 100644 internal/core/data/operators/reading/mocks/Loader.go delete mode 100644 internal/core/data/operators/value_descriptor/db.go delete mode 100644 internal/core/data/operators/value_descriptor/get.go delete mode 100644 internal/core/data/operators/value_descriptor/get_test.go delete mode 100644 internal/core/data/operators/value_descriptor/mocks/Loader.go delete mode 100644 internal/core/data/reading.go delete mode 100644 internal/core/data/reading_test.go delete mode 100644 internal/core/data/router.go delete mode 100644 internal/core/data/router_test.go delete mode 100644 internal/core/data/utils.go delete mode 100644 internal/core/data/validate.go delete mode 100644 internal/core/data/validate_test.go delete mode 100644 internal/core/data/valuedescriptor.go delete mode 100644 internal/core/data/valuedescriptor_test.go delete mode 100644 internal/core/metadata/container/coredata.go delete mode 100644 internal/core/metadata/container/notifications.go delete mode 100644 internal/core/metadata/errors/types.go delete mode 100644 internal/core/metadata/interfaces/db.go delete mode 100644 internal/core/metadata/interfaces/mocks/DBClient.go delete mode 100644 internal/core/metadata/operators/addressable/add.go delete mode 100644 internal/core/metadata/operators/addressable/add_test.go delete mode 100644 internal/core/metadata/operators/addressable/db.go delete mode 100644 internal/core/metadata/operators/addressable/delete.go delete mode 100644 internal/core/metadata/operators/addressable/delete_test.go delete mode 100644 internal/core/metadata/operators/addressable/get.go delete mode 100644 internal/core/metadata/operators/addressable/get_test.go delete mode 100644 internal/core/metadata/operators/addressable/mocks/AddressDeleter.go delete mode 100644 internal/core/metadata/operators/addressable/mocks/AddressLoader.go delete mode 100644 internal/core/metadata/operators/addressable/mocks/AddressUpdater.go delete mode 100644 internal/core/metadata/operators/addressable/mocks/AddressWriter.go delete mode 100644 internal/core/metadata/operators/addressable/update.go delete mode 100644 internal/core/metadata/operators/addressable/update_test.go delete mode 100644 internal/core/metadata/operators/command/db.go delete mode 100644 internal/core/metadata/operators/command/get.go delete mode 100644 internal/core/metadata/operators/command/get_test.go delete mode 100644 internal/core/metadata/operators/command/mocks/CommandLoader.go delete mode 100644 internal/core/metadata/operators/device/add.go delete mode 100644 internal/core/metadata/operators/device/add_test.go delete mode 100644 internal/core/metadata/operators/device/db.go delete mode 100644 internal/core/metadata/operators/device/events.go delete mode 100644 internal/core/metadata/operators/device/get.go delete mode 100644 internal/core/metadata/operators/device/get_test.go delete mode 100644 internal/core/metadata/operators/device/mocks/DeviceAdder.go delete mode 100644 internal/core/metadata/operators/device/mocks/DeviceLoader.go delete mode 100644 internal/core/metadata/operators/device/mocks/DeviceProfileDeleter.go delete mode 100644 internal/core/metadata/operators/device/mocks/DeviceProfileUpdater.go delete mode 100644 internal/core/metadata/operators/device/mocks/DeviceServiceLoader.go delete mode 100644 internal/core/metadata/operators/device/mocks/DeviceUpdater.go delete mode 100644 internal/core/metadata/operators/device/notify.go delete mode 100644 internal/core/metadata/operators/device/notify_test.go delete mode 100644 internal/core/metadata/operators/device/request.go delete mode 100644 internal/core/metadata/operators/device/request_test.go delete mode 100644 internal/core/metadata/operators/device/update.go delete mode 100644 internal/core/metadata/operators/device/update_test.go delete mode 100644 internal/core/metadata/operators/device_profile/add.go delete mode 100644 internal/core/metadata/operators/device_profile/add_test.go delete mode 100644 internal/core/metadata/operators/device_profile/db.go delete mode 100644 internal/core/metadata/operators/device_profile/delete.go delete mode 100644 internal/core/metadata/operators/device_profile/delete_test.go delete mode 100644 internal/core/metadata/operators/device_profile/get.go delete mode 100644 internal/core/metadata/operators/device_profile/get_test.go delete mode 100644 internal/core/metadata/operators/device_profile/mocks/DeviceProfileAdder.go delete mode 100644 internal/core/metadata/operators/device_profile/mocks/DeviceProfileDeleter.go delete mode 100644 internal/core/metadata/operators/device_profile/mocks/DeviceProfileLoader.go delete mode 100644 internal/core/metadata/operators/device_profile/mocks/DeviceProfileUpdater.go delete mode 100644 internal/core/metadata/operators/device_profile/mocks/ValueDescriptorAdder.go delete mode 100644 internal/core/metadata/operators/device_profile/mocks/ValueDescriptorUpdater.go delete mode 100644 internal/core/metadata/operators/device_profile/update.go delete mode 100644 internal/core/metadata/operators/device_profile/update_test.go delete mode 100644 internal/core/metadata/operators/device_profile/value_descriptor.go delete mode 100644 internal/core/metadata/operators/device_profile/value_descriptor_test.go delete mode 100644 internal/core/metadata/operators/device_service/db.go delete mode 100644 internal/core/metadata/operators/device_service/get.go delete mode 100644 internal/core/metadata/operators/device_service/get_test.go delete mode 100644 internal/core/metadata/operators/device_service/mocks/DeviceServiceLoader.go delete mode 100644 internal/core/metadata/operators/device_service/mocks/DeviceServiceUpdater.go delete mode 100644 internal/core/metadata/operators/device_service/update.go delete mode 100644 internal/core/metadata/operators/device_service/update_test.go delete mode 100644 internal/core/metadata/rest_addressable.go delete mode 100644 internal/core/metadata/rest_addressable_test.go delete mode 100644 internal/core/metadata/rest_command.go delete mode 100644 internal/core/metadata/rest_command_test.go delete mode 100644 internal/core/metadata/rest_device.go delete mode 100644 internal/core/metadata/rest_device_test.go delete mode 100644 internal/core/metadata/rest_deviceprofile.go delete mode 100644 internal/core/metadata/rest_deviceprofile_test.go delete mode 100644 internal/core/metadata/rest_devicereport.go delete mode 100644 internal/core/metadata/rest_deviceservice.go delete mode 100644 internal/core/metadata/rest_deviceservice_test.go delete mode 100644 internal/core/metadata/rest_provisionwatcher.go delete mode 100644 internal/core/metadata/router.go delete mode 100644 internal/core/metadata/utils.go delete mode 100644 internal/pkg/bootstrap/container/database.go delete mode 100644 internal/pkg/bootstrap/handlers/database/database.go delete mode 100644 internal/pkg/container/errorHandler.go delete mode 100644 internal/pkg/correlation/models/event.go delete mode 100644 internal/pkg/db/interfaces/db.go delete mode 100644 internal/pkg/db/redis/CONFIGURATION.md delete mode 100644 internal/pkg/db/redis/README.md delete mode 100644 internal/pkg/db/redis/client_integration_test.go delete mode 100644 internal/pkg/db/redis/data.go delete mode 100644 internal/pkg/db/redis/device.go delete mode 100644 internal/pkg/db/redis/device_profile.go delete mode 100644 internal/pkg/db/redis/device_service.go delete mode 100644 internal/pkg/db/redis/event.go delete mode 100644 internal/pkg/db/redis/metadata.go delete mode 100644 internal/pkg/db/redis/models/db_command.go delete mode 100644 internal/pkg/db/redis/models/interval.go delete mode 100644 internal/pkg/db/redis/models/interval_action.go delete mode 100644 internal/pkg/db/redis/notifications.go delete mode 100644 internal/pkg/db/redis/object.go delete mode 100644 internal/pkg/db/redis/provision_watcher.go delete mode 100644 internal/pkg/db/redis/queries.go delete mode 100644 internal/pkg/db/redis/scheduler.go delete mode 100644 internal/pkg/db/redis/scripts.go delete mode 100644 internal/pkg/db/redis/util.go delete mode 100644 internal/pkg/db/test/db_data.go delete mode 100644 internal/pkg/db/test/db_metadata.go delete mode 100644 internal/pkg/db/test/db_notifications.go delete mode 100644 internal/pkg/db/test/db_scheduler.go delete mode 100644 internal/pkg/errorconcept/addressable.go delete mode 100644 internal/pkg/errorconcept/cbor.go delete mode 100644 internal/pkg/errorconcept/command.go delete mode 100644 internal/pkg/errorconcept/common.go delete mode 100644 internal/pkg/errorconcept/const.go delete mode 100644 internal/pkg/errorconcept/db.go delete mode 100644 internal/pkg/errorconcept/default.go delete mode 100644 internal/pkg/errorconcept/device.go delete mode 100644 internal/pkg/errorconcept/device_profile.go delete mode 100644 internal/pkg/errorconcept/device_report.go delete mode 100644 internal/pkg/errorconcept/device_service.go delete mode 100644 internal/pkg/errorconcept/event.go delete mode 100644 internal/pkg/errorconcept/handler.go delete mode 100644 internal/pkg/errorconcept/provision_watcher.go delete mode 100644 internal/pkg/errorconcept/service_client.go delete mode 100644 internal/pkg/errorconcept/value_descriptors.go delete mode 100644 internal/support/notifications/cleanup.go delete mode 100644 internal/support/notifications/const.go delete mode 100644 internal/support/notifications/distribution_coordinator.go delete mode 100644 internal/support/notifications/errors/types.go delete mode 100644 internal/support/notifications/escalating_service.go delete mode 100644 internal/support/notifications/interfaces/db.go delete mode 100644 internal/support/notifications/interfaces/mocks/DBClient.go delete mode 100644 internal/support/notifications/normal_distribution.go delete mode 100644 internal/support/notifications/operators/notification/db.go delete mode 100644 internal/support/notifications/operators/notification/delete.go delete mode 100644 internal/support/notifications/operators/notification/delete_test.go delete mode 100644 internal/support/notifications/operators/notification/get.go delete mode 100644 internal/support/notifications/operators/notification/get_test.go delete mode 100644 internal/support/notifications/operators/notification/mocks/NotificationDeleter.go delete mode 100644 internal/support/notifications/operators/notification/mocks/NotificationLoader.go delete mode 100644 internal/support/notifications/operators/subscription/add.go delete mode 100644 internal/support/notifications/operators/subscription/add_test.go delete mode 100644 internal/support/notifications/operators/subscription/db.go delete mode 100644 internal/support/notifications/operators/subscription/delete.go delete mode 100644 internal/support/notifications/operators/subscription/delete_test.go delete mode 100644 internal/support/notifications/operators/subscription/get.go delete mode 100644 internal/support/notifications/operators/subscription/get_test.go delete mode 100644 internal/support/notifications/operators/subscription/mocks/SubscriptionDeleter.go delete mode 100644 internal/support/notifications/operators/subscription/mocks/SubscriptionLoader.go delete mode 100644 internal/support/notifications/operators/subscription/mocks/SubscriptionUpdater.go delete mode 100644 internal/support/notifications/operators/subscription/mocks/SubscriptionWriter.go delete mode 100644 internal/support/notifications/operators/subscription/update.go delete mode 100644 internal/support/notifications/operators/subscription/update_test.go delete mode 100644 internal/support/notifications/rest_notification.go delete mode 100644 internal/support/notifications/rest_notification_test.go delete mode 100644 internal/support/notifications/rest_subscription.go delete mode 100644 internal/support/notifications/rest_subscription_test.go delete mode 100644 internal/support/notifications/rest_transmission.go delete mode 100644 internal/support/notifications/router.go delete mode 100644 internal/support/notifications/sending_service.go delete mode 100644 internal/support/notifications/sending_service_test.go delete mode 100644 internal/support/notifications/utils.go delete mode 100644 internal/support/scheduler/const.go delete mode 100644 internal/support/scheduler/container/queue.go delete mode 100644 internal/support/scheduler/errors/types.go delete mode 100644 internal/support/scheduler/interfaces/db.go delete mode 100644 internal/support/scheduler/interfaces/mocks/DBClient.go delete mode 100644 internal/support/scheduler/interfaces/mocks/SchedulerQueueClient.go delete mode 100644 internal/support/scheduler/interfaces/scheduler_queue.go delete mode 100644 internal/support/scheduler/interval.go delete mode 100644 internal/support/scheduler/interval_action.go delete mode 100644 internal/support/scheduler/interval_action_test.go delete mode 100644 internal/support/scheduler/interval_test.go delete mode 100644 internal/support/scheduler/loader.go delete mode 100644 internal/support/scheduler/operators/interval/add.go delete mode 100644 internal/support/scheduler/operators/interval/add_test.go delete mode 100644 internal/support/scheduler/operators/interval/db.go delete mode 100644 internal/support/scheduler/operators/interval/delete.go delete mode 100644 internal/support/scheduler/operators/interval/delete_test.go delete mode 100644 internal/support/scheduler/operators/interval/get.go delete mode 100644 internal/support/scheduler/operators/interval/get_test.go delete mode 100644 internal/support/scheduler/operators/interval/mocks/IntervalActionLoader.go delete mode 100644 internal/support/scheduler/operators/interval/mocks/IntervalDeleter.go delete mode 100644 internal/support/scheduler/operators/interval/mocks/IntervalLoader.go delete mode 100644 internal/support/scheduler/operators/interval/mocks/IntervalUpdater.go delete mode 100644 internal/support/scheduler/operators/interval/mocks/IntervalWriter.go delete mode 100644 internal/support/scheduler/operators/interval/mocks/SchedulerQueueDeleter.go delete mode 100644 internal/support/scheduler/operators/interval/mocks/SchedulerQueueLoader.go delete mode 100644 internal/support/scheduler/operators/interval/mocks/SchedulerQueueUpdater.go delete mode 100644 internal/support/scheduler/operators/interval/mocks/SchedulerQueueWriter.go delete mode 100644 internal/support/scheduler/operators/interval/update.go delete mode 100644 internal/support/scheduler/operators/interval/update_test.go delete mode 100644 internal/support/scheduler/operators/intervalaction/add.go delete mode 100644 internal/support/scheduler/operators/intervalaction/add_test.go delete mode 100644 internal/support/scheduler/operators/intervalaction/db.go delete mode 100644 internal/support/scheduler/operators/intervalaction/get.go delete mode 100644 internal/support/scheduler/operators/intervalaction/get_test.go delete mode 100644 internal/support/scheduler/operators/intervalaction/mocks/IntervalActionLoader.go delete mode 100644 internal/support/scheduler/operators/intervalaction/mocks/IntervalActionWriter.go delete mode 100644 internal/support/scheduler/operators/intervalaction/mocks/SchedulerQueueWriter.go delete mode 100644 internal/support/scheduler/rest_interval.go delete mode 100644 internal/support/scheduler/rest_interval_test.go delete mode 100644 internal/support/scheduler/rest_intervalaction.go delete mode 100644 internal/support/scheduler/rest_intervalaction_test.go delete mode 100644 internal/support/scheduler/router.go delete mode 100644 internal/support/scheduler/schedulercontext_test.go delete mode 100644 openapi/v1/core-command.yaml delete mode 100644 openapi/v1/core-data.yaml delete mode 100644 openapi/v1/core-metadata.yaml delete mode 100644 openapi/v1/support-logging.yaml delete mode 100644 openapi/v1/support-notifications.yaml delete mode 100644 openapi/v1/support-scheduler.yaml delete mode 100644 openapi/v1/system-agent.yaml diff --git a/Makefile b/Makefile index 7c12db4fcc..d8a2608eba 100644 --- a/Makefile +++ b/Makefile @@ -99,7 +99,6 @@ test: GO111MODULE=on go vet ./... gofmt -l . [ "`gofmt -l .`" = "" ] - ./bin/test-go-mod-tidy.sh ./bin/test-attribution-txt.sh run: diff --git a/bin/edgex-docker-launch.sh b/bin/edgex-docker-launch.sh deleted file mode 100755 index d9d2b50314..0000000000 --- a/bin/edgex-docker-launch.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env sh - -# Copyright 2020 Redis Labs Inc. -# -# 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. - -# Usage: bin/edgex-docker-launch.sh -# -# By default download the Redis based Docker Compose file and attempt to start EdgeX. -# -# To override the compose file entirely set the COMPOSE_FILE_PATH environment variable to the full -# pathname of the compose file you want to use. - -RELEASE=nightly-build -VERSION=nexus -GITHUB_PATH=https://raw.githubusercontent.com/edgexfoundry/developer-scripts/master/releases/"${RELEASE}"/compose-files -PERSIST=${1:-redis} - -if [ -z "${COMPOSE_FILE_PATH}" ]; then - COMPOSE_FILE=docker-compose-${VERSION}-${PERSIST}-no-secty.yml - - COMPOSE_FILE_PATH=/tmp/${COMPOSE_FILE} - mkdir -p "$(dirname "${COMPOSE_FILE_PATH}")" - echo "Downloading Docker Compose file..." - curl -s -o "${COMPOSE_FILE_PATH}" ${GITHUB_PATH}/"${COMPOSE_FILE}" -fi - -echo "Starting EdgeX..." -docker-compose -f "${COMPOSE_FILE_PATH}" up -d diff --git a/bin/edgex-launch.sh b/bin/edgex-launch.sh deleted file mode 100755 index fe9d4cae4e..0000000000 --- a/bin/edgex-launch.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/bash -# -# Copyright (c) 2018 -# Mainflux -# -# SPDX-License-Identifier: Apache-2.0 -# - -### -# Launches all EdgeX Go binaries (must be previously built). -# -# Expects that Consul and Redis are already installed and running. -# -### - -DIR=$PWD -CMD=../cmd - -# Kill all edgex-* stuff -function cleanup { - pkill edgex -} - -# disable secret-store integration -export EDGEX_SECURITY_SECRET_STORE=false - -### -# Support logging -### -cd $CMD/support-logging -# Add `edgex-` prefix on start, so we can find the process family -exec -a edgex-support-logging ./support-logging & -cd $DIR - -### -# Core Command -### -cd $CMD/core-command -# Add `edgex-` prefix on start, so we can find the process family -exec -a edgex-core-command ./core-command & -cd $DIR - -### -# Core Data -### -cd $CMD/core-data -exec -a edgex-core-data ./core-data & -cd $DIR - -### -# Core Metadata -### -cd $CMD/core-metadata -exec -a edgex-core-metadata ./core-metadata & -cd $DIR - -### -# Support Notifications -### -cd $CMD/support-notifications -# Add `edgex-` prefix on start, so we can find the process family -exec -a edgex-support-notifications ./support-notifications & -cd $DIR - -### -# System Management Agent -### -cd $CMD/sys-mgmt-agent -# Add `edgex-` prefix on start, so we can find the process family -exec -a edgex-sys-mgmt-agent ./sys-mgmt-agent & -cd $DIR - -# Support Scheduler -### -cd $CMD/support-scheduler -# Add `edgex-` prefix on start, so we can find the process family -exec -a edgex-support-scheduler ./support-scheduler & -cd $DIR - -trap cleanup EXIT - -while : ; do sleep 1 ; done \ No newline at end of file diff --git a/bin/test-go-mod-tidy.sh b/bin/test-go-mod-tidy.sh deleted file mode 100755 index 8599e5346c..0000000000 --- a/bin/test-go-mod-tidy.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash -e - -# get the directory of this script -# snippet from https://stackoverflow.com/a/246128/10102404 -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" -GIT_ROOT=$(dirname "$SCRIPT_DIR") - -EXIT_CODE=0 - -cd "$GIT_ROOT" - -if [ -f go.mod.bk ]; then - echo "go.mod.bk exits - remove before continuing" - exit 1 -fi - -# backup go.mod -cp go.mod go.mod.bk - -trap cleanup 1 2 3 6 - -cleanup() -{ - cd "$GIT_ROOT" - # restore the go.mod file dir - rm go.mod - if [ -f go.mod.bk ]; then - mv go.mod.bk go.mod - fi - exit $EXIT_CODE -} - -# if go.mod doesn't exist then fail -if [ ! -f go.mod ]; then - echo "missing go.mod, please fix" - EXIT_CODE=1 - cleanup -fi - -GO111MODULE=on go mod tidy - -# check if go.mod and go.mod.bk are the same - -set +e -changes=$(diff -u go.mod go.mod.bk) -set -e - -if [ -n "$changes" ]; then - echo "go.mod is not tidy, please run \"go mod tidy\"" - echo "changes from running \"go mod tidy:\"" - echo "$changes" - EXIT_CODE=1 -fi - -cleanup diff --git a/cmd/core-command/main.go b/cmd/core-command/main.go index 4181973164..61fe9d827a 100644 --- a/cmd/core-command/main.go +++ b/cmd/core-command/main.go @@ -24,5 +24,5 @@ import ( func main() { ctx, cancel := context.WithCancel(context.Background()) - command.Main(ctx, cancel, mux.NewRouter(), nil) + command.Main(ctx, cancel, mux.NewRouter()) } diff --git a/cmd/core-data/main.go b/cmd/core-data/main.go index 9845fca057..e8535bddef 100644 --- a/cmd/core-data/main.go +++ b/cmd/core-data/main.go @@ -24,5 +24,5 @@ import ( func main() { ctx, cancel := context.WithCancel(context.Background()) - data.Main(ctx, cancel, mux.NewRouter(), nil) + data.Main(ctx, cancel, mux.NewRouter()) } diff --git a/cmd/core-metadata/main.go b/cmd/core-metadata/main.go index 2ab5b4ece5..70409ddc32 100644 --- a/cmd/core-metadata/main.go +++ b/cmd/core-metadata/main.go @@ -23,5 +23,5 @@ import ( func main() { ctx, cancel := context.WithCancel(context.Background()) - metadata.Main(ctx, cancel, mux.NewRouter(), nil) + metadata.Main(ctx, cancel, mux.NewRouter()) } diff --git a/cmd/security-bootstrapper/main.go b/cmd/security-bootstrapper/main.go index 97e541e7a8..b9734d8b25 100644 --- a/cmd/security-bootstrapper/main.go +++ b/cmd/security-bootstrapper/main.go @@ -19,7 +19,6 @@ import ( "os" "github.com/edgexfoundry/edgex-go/internal/security/bootstrapper" - "github.com/gorilla/mux" ) func main() { @@ -29,5 +28,5 @@ func main() { _ = os.Unsetenv("EDGEX_PROFILE") ctx, cancel := context.WithCancel(context.Background()) - bootstrapper.Main(ctx, cancel, mux.NewRouter(), nil) + bootstrapper.Main(ctx, cancel) } diff --git a/cmd/security-file-token-provider/main.go b/cmd/security-file-token-provider/main.go index 6806614737..d4fd08e0b4 100644 --- a/cmd/security-file-token-provider/main.go +++ b/cmd/security-file-token-provider/main.go @@ -19,11 +19,9 @@ import ( "context" "github.com/edgexfoundry/edgex-go/internal/security/fileprovider" - - "github.com/gorilla/mux" ) func main() { ctx, cancel := context.WithCancel(context.Background()) - fileprovider.Main(ctx, cancel, mux.NewRouter(), nil) + fileprovider.Main(ctx, cancel) } diff --git a/cmd/security-proxy-setup/main.go b/cmd/security-proxy-setup/main.go index 24e92f33d6..dc539ef000 100644 --- a/cmd/security-proxy-setup/main.go +++ b/cmd/security-proxy-setup/main.go @@ -19,11 +19,9 @@ import ( "context" "github.com/edgexfoundry/edgex-go/internal/security/proxy" - - "github.com/gorilla/mux" ) func main() { ctx, cancel := context.WithCancel(context.Background()) - proxy.Main(ctx, cancel, mux.NewRouter(), nil) + proxy.Main(ctx, cancel) } diff --git a/cmd/security-secretstore-setup/main.go b/cmd/security-secretstore-setup/main.go index 0a11ad7f8a..1d68fa9e3b 100644 --- a/cmd/security-secretstore-setup/main.go +++ b/cmd/security-secretstore-setup/main.go @@ -23,11 +23,9 @@ import ( "context" "github.com/edgexfoundry/edgex-go/internal/security/secretstore" - - "github.com/gorilla/mux" ) func main() { ctx, cancel := context.WithCancel(context.Background()) - secretstore.Main(ctx, cancel, mux.NewRouter(), nil) + secretstore.Main(ctx, cancel) } diff --git a/cmd/support-notifications/main.go b/cmd/support-notifications/main.go index b19b39f008..2514e5ae40 100644 --- a/cmd/support-notifications/main.go +++ b/cmd/support-notifications/main.go @@ -30,5 +30,5 @@ import ( func main() { ctx, cancel := context.WithCancel(context.Background()) - notifications.Main(ctx, cancel, mux.NewRouter(), nil) + notifications.Main(ctx, cancel, mux.NewRouter()) } diff --git a/cmd/support-scheduler/main.go b/cmd/support-scheduler/main.go index 7db5cf2eb3..41381753e7 100644 --- a/cmd/support-scheduler/main.go +++ b/cmd/support-scheduler/main.go @@ -24,5 +24,5 @@ import ( func main() { ctx, cancel := context.WithCancel(context.Background()) - scheduler.Main(ctx, cancel, mux.NewRouter(), nil) + scheduler.Main(ctx, cancel, mux.NewRouter()) } diff --git a/cmd/sys-mgmt-agent/main.go b/cmd/sys-mgmt-agent/main.go index 308a5cfdfc..f928f19658 100644 --- a/cmd/sys-mgmt-agent/main.go +++ b/cmd/sys-mgmt-agent/main.go @@ -23,5 +23,5 @@ import ( func main() { ctx, cancel := context.WithCancel(context.Background()) - agent.Main(ctx, cancel, mux.NewRouter(), nil) + agent.Main(ctx, cancel, mux.NewRouter()) } diff --git a/go.mod b/go.mod index dfcdd48391..ad859669b5 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,6 @@ module github.com/edgexfoundry/edgex-go require ( bitbucket.org/bertimus9/systemstat v0.0.0-20180207000608-0eeff89b0690 - github.com/OneOfOne/xxhash v1.2.8 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/edgexfoundry/go-mod-bootstrap/v2 v2.0.0-dev.61 github.com/edgexfoundry/go-mod-configuration/v2 v2.0.0-dev.8 @@ -14,11 +13,8 @@ require ( github.com/gomodule/redigo v2.0.0+incompatible github.com/google/uuid v1.2.0 github.com/gorilla/mux v1.8.0 - github.com/imdario/mergo v0.3.12 github.com/lib/pq v1.10.2 github.com/pelletier/go-toml v1.9.1 - github.com/pkg/errors v0.8.1 - github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 gopkg.in/eapache/queue.v1 v1.1.0 diff --git a/internal/core/command/README.md b/internal/core/command/README.md index 8fea395fe6..2825f4d11d 100644 --- a/internal/core/command/README.md +++ b/internal/core/command/README.md @@ -20,7 +20,7 @@ cd $GOPATH/src/github.com/edgexfoundry/edgex-go # pull the 3rd party / vendor packages make prepare # build the microservice -make cmd/core-command/core-command +make core-command # get to the command microservice executable cd cmd/core-command # run the microservice (may require other dependent services to run correctly) diff --git a/internal/core/command/const.go b/internal/core/command/const.go deleted file mode 100644 index 265cf82490..0000000000 --- a/internal/core/command/const.go +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Inc. - * - * 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 command - -const ( - ID = "id" - NAME = "name" - DEVICEIDURLPARAM = "{deviceId}" - COMMAND = "command" - COMMANDID = "commandid" - COMMANDNAME = "commandname" - DEVICE = "device" -) diff --git a/internal/core/command/container/device.go b/internal/core/command/container/device.go deleted file mode 100644 index 99dbed2128..0000000000 --- a/internal/core/command/container/device.go +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 container - -import ( - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/metadata" - - "github.com/edgexfoundry/go-mod-bootstrap/v2/di" -) - -// MetadataDeviceClientName contains the name of the client implementation in the DIC. -var MetadataDeviceClientName = di.TypeInstanceToName((*metadata.DeviceClient)(nil)) - -// MetadataDeviceClientFrom helper function queries the DIC and returns the client implementation. -func MetadataDeviceClientFrom(get di.Get) metadata.DeviceClient { - return get(MetadataDeviceClientName).(metadata.DeviceClient) -} diff --git a/internal/core/command/device.go b/internal/core/command/device.go deleted file mode 100644 index 5faa4816fe..0000000000 --- a/internal/core/command/device.go +++ /dev/null @@ -1,235 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * - * 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 command - -import ( - "bytes" - "context" - "fmt" - "net/http" - - "github.com/edgexfoundry/edgex-go/internal" - "github.com/edgexfoundry/edgex-go/internal/core/command/config" - "github.com/edgexfoundry/edgex-go/internal/core/command/errors" - "github.com/edgexfoundry/edgex-go/internal/core/command/interfaces" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/metadata" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" -) - -func executeCommandByDeviceID( - originalRequest *http.Request, - body string, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - deviceClient metadata.DeviceClient, - httpCaller internal.HttpCaller) (deviceServiceResponse *http.Response, theResponseBody string, failure error) { - - if originalRequest == nil { - return nil, "", errors.NewErrExtractingInfoFromRequest() - } - - ctx := originalRequest.Context() - deviceID, commandID, err := extractDeviceIdAndCommandIdFromRequest(originalRequest) - if err != nil { - return nil, "", err - } - - d, err := deviceClient.Device(ctx, deviceID) - if err != nil { - return nil, "", err - } - - if d.AdminState == contract.Locked { - return nil, "", errors.NewErrDeviceLocked(d.Name) - } - - // once command service have its own persistence layer this call will be changed. - commands, err := dbClient.GetCommandsByDeviceId(d.Id) - if err != nil { - return nil, "", err - } - - var c contract.Command - for _, command := range commands { - if commandID == command.Id { - c = command - break - } - } - - if c.String() == (contract.Command{}).String() { - return nil, "", errors.NewErrCommandNotAssociatedWithDevice(commandID, deviceID) - } - - return executeCommandByDevice(ctx, d, c, body, lc, originalRequest, httpCaller) -} - -// extractDeviceIdAndCommandIdFromRequest extracts deviceID and commandID from r, which -// is the HTTP request parameter, and returns the deviceID, commandID to caller, or, if not -// successfully extracted, the associated error is returned. -func extractDeviceIdAndCommandIdFromRequest(r *http.Request) (string, string, error) { - vars := mux.Vars(r) - deviceID := vars[ID] - commandID := vars[COMMANDID] - - if deviceID == "" || commandID == "" { - return deviceID, commandID, errors.NewErrExtractingInfoFromRequest() - } - - return deviceID, commandID, nil -} - -func executeCommandByName( - originalRequest *http.Request, - ctx context.Context, - dn string, - cn string, - body string, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - deviceClient metadata.DeviceClient, - httpCaller internal.HttpCaller) (deviceServiceResponse *http.Response, theResponseBody string, failure error) { - - d, err := deviceClient.DeviceForName(ctx, dn) - if err != nil { - return nil, "", err - } - - if d.AdminState == contract.Locked { - return nil, "", errors.NewErrDeviceLocked(d.Name) - } - - command, err := dbClient.GetCommandByNameAndDeviceId(cn, d.Id) - if err != nil { - return nil, "", err - } - - return executeCommandByDevice(ctx, d, command, body, lc, originalRequest, httpCaller) -} - -func executeCommandByDevice( - ctx context.Context, - device contract.Device, - command contract.Command, - body string, - lc logger.LoggingClient, - originalRequest *http.Request, - httpCaller internal.HttpCaller) (deviceServiceResponse *http.Response, theResponseBody string, failure error) { - - var method string - var ex Executor - var err error - - if originalRequest == nil { - return nil, "", errors.NewErrParsingOriginalRequest("method") - } - - switch originalRequest.Method { - case http.MethodPut: - ex, err = NewPutCommand(device, command, body, ctx, httpCaller, lc, originalRequest) - case http.MethodGet: - ex, err = NewGetCommand(device, command, ctx, httpCaller, lc, originalRequest) - default: - lc.Error(fmt.Sprintf("unknown method: %s", method)) - } - - if err != nil { - return nil, "", err - } - - deviceServiceResponse, err = ex.Execute() - if err != nil { - return nil, "", err - } - - responseBody := new(bytes.Buffer) - _, readErr := responseBody.ReadFrom(deviceServiceResponse.Body) - if readErr != nil { - return nil, "", readErr - } - - return deviceServiceResponse, responseBody.String(), nil -} - -func getAllCommands( - ctx context.Context, - dbClient interfaces.DBClient, - deviceClient metadata.DeviceClient, - configuration *config.ConfigurationStruct) ([]contract.CommandResponse, error) { - - devices, err := deviceClient.Devices(ctx) - if err != nil { - return nil, err - } - - var responses []contract.CommandResponse - for _, d := range devices { - cr, err := newCommandResponse(d, dbClient, configuration) - if err != nil { - return nil, err - } - - responses = append(responses, cr) - } - - return responses, nil -} - -func getCommandsByDeviceID( - ctx context.Context, - did string, - dbClient interfaces.DBClient, - deviceClient metadata.DeviceClient, - configuration *config.ConfigurationStruct) (contract.CommandResponse, error) { - - d, err := deviceClient.Device(ctx, did) - if err != nil { - return contract.CommandResponse{}, err - } - - return newCommandResponse(d, dbClient, configuration) -} - -func getCommandsByDeviceName( - ctx context.Context, - dn string, - dbClient interfaces.DBClient, - deviceClient metadata.DeviceClient, - configuration *config.ConfigurationStruct) (contract.CommandResponse, error) { - - d, err := deviceClient.DeviceForName(ctx, dn) - if err != nil { - return contract.CommandResponse{}, err - } - - return newCommandResponse(d, dbClient, configuration) -} - -func newCommandResponse( - d contract.Device, - dbClient interfaces.DBClient, - configuration *config.ConfigurationStruct) (contract.CommandResponse, error) { - - commands, err := dbClient.GetCommandsByDeviceId(d.Id) - if err != nil { - return contract.CommandResponse{}, err - } - - return contract.CommandResponseFromDevice(d, commands, configuration.Service.Url()), nil -} diff --git a/internal/core/command/device_test.go b/internal/core/command/device_test.go deleted file mode 100644 index d8eff63e5c..0000000000 --- a/internal/core/command/device_test.go +++ /dev/null @@ -1,186 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 command - -import ( - goErrors "errors" - "net/http" - "net/url" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/core/command/errors" - "github.com/edgexfoundry/edgex-go/internal/core/command/interfaces" - "github.com/edgexfoundry/edgex-go/internal/core/command/interfaces/mocks" - mdMocks "github.com/edgexfoundry/edgex-go/internal/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/types" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" -) - -var ( - ExistingDeviceID = "existing device id" - DeviceIDWithAssociatedInvalidObjectID = "device id with associated invalid object id" - NonExistentDeviceID = "non existent device id" - DeviceIDResultingInInternalServerError = "device id resulting in internal server error" - DeviceIDForLockedResource = "device id for locked resource" - MismatchedDeviceID = "mismatched device id" - DeviceIDd200c404 = "device id d200-c404" - DeviceIDd200c500 = "device id d200-c500" - DeviceIDd200c200 = "device id d200-c200" - TestCommandID = "test command id" - TestDeviceID = "test device id" -) - -func createTestDeviceWithPathUrl(commandID string, deviceID string) models.Device { - - return models.Device{ - AdminState: models.Unlocked, - Service: models.DeviceService{ - Addressable: models.Addressable{ - Path: "/api/v1/device/" + deviceID + "/command/" + commandID}}} -} - -func createDeviceRequestWithPathParameters( - sampleDevice models.Device, - params map[string]string) *http.Request { - - req, _ := http.NewRequest(http.MethodGet, cmdURI, nil) - req.URL.Path = sampleDevice.Service.Addressable.Path - - return mux.SetURLVars(req, params) -} - -// commandByDeviceID -func TestExecuteGETCommandByDeviceIDAndCommandID(t *testing.T) { - tests := []struct { - name string - status string - request *http.Request - expectedErr error - }{ - { - "device with invalid object id", - DeviceIDWithAssociatedInvalidObjectID, - createDeviceRequestWithPathParameters( - createTestDeviceWithPathUrl(TestCommandID, DeviceIDWithAssociatedInvalidObjectID), - map[string]string{ID: DeviceIDWithAssociatedInvalidObjectID, COMMANDID: TestCommandID}), - types.NewErrServiceClient(400, []byte("Invalid object ID")), - }, - { - "device not found", - NonExistentDeviceID, - createDeviceRequestWithPathParameters( - createTestDeviceWithPathUrl(TestCommandID, NonExistentDeviceID), - map[string]string{ID: NonExistentDeviceID, COMMANDID: TestCommandID}), - types.NewErrServiceClient(http.StatusNotFound, []byte{}), - }, - { - "device resulting in internal server error", - DeviceIDResultingInInternalServerError, - createDeviceRequestWithPathParameters( - createTestDeviceWithPathUrl(TestCommandID, DeviceIDResultingInInternalServerError), - map[string]string{ID: DeviceIDResultingInInternalServerError, COMMANDID: TestCommandID}), - goErrors.New("unexpected error"), - }, - { - "device was locked", - DeviceIDForLockedResource, - createDeviceRequestWithPathParameters( - createTestDeviceWithPathUrl(TestCommandID, DeviceIDForLockedResource), - map[string]string{ID: DeviceIDForLockedResource, COMMANDID: TestCommandID}), - errors.NewErrDeviceLocked(""), - }, - { - "command was not found", - DeviceIDd200c404, - createDeviceRequestWithPathParameters( - createTestDeviceWithPathUrl(TestCommandID, DeviceIDd200c404), - map[string]string{ID: DeviceIDd200c404, COMMANDID: DeviceIDWithAssociatedInvalidObjectID}), - db.ErrNotFound, - }, - { - "command could not be handled", - DeviceIDd200c500, - createDeviceRequestWithPathParameters( - createTestDeviceWithPathUrl(TestCommandID, DeviceIDd200c500), - map[string]string{ID: DeviceIDd200c500, COMMANDID: DeviceIDResultingInInternalServerError}), - goErrors.New("unexpected error"), - }, - { - "command did not belong to device", - MismatchedDeviceID, - createDeviceRequestWithPathParameters( - createTestDeviceWithPathUrl(TestCommandID, MismatchedDeviceID), - map[string]string{ID: MismatchedDeviceID, COMMANDID: TestCommandID}), - errors.NewErrCommandNotAssociatedWithDevice(TestCommandID, MismatchedDeviceID), - }, { - "command could not be parsed", - DeviceIDd200c200, - createDeviceRequestWithPathParameters( - createTestDeviceWithPathUrl(ExistingDeviceID, DeviceIDd200c200), - map[string]string{ID: DeviceIDd200c200, COMMANDID: ExistingDeviceID}), - &url.Error{Op: "parse", URL: "://:0?", Err: goErrors.New("missing protocol scheme")}, - }, - } - httpCaller := createMockHttpCaller() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, _, actualErr := executeCommandByDeviceID( - tt.request, - "", - logger.NewMockClient(), - newMockDBClient(), - newMockDeviceClient(), - httpCaller) - if actualErr == nil { - t.Fatal("expected error") - } - require.Equal(t, tt.expectedErr.Error(), actualErr.Error()) - }) - } -} - -func newMockDeviceClient() *mdMocks.DeviceClient { - client := mdMocks.DeviceClient{} - client.On("Device", mock.Anything, DeviceIDWithAssociatedInvalidObjectID).Return(contract.Device{}, types.NewErrServiceClient(400, []byte("Invalid object ID"))) - client.On("Device", mock.Anything, NonExistentDeviceID).Return(contract.Device{}, types.NewErrServiceClient(http.StatusNotFound, []byte{})) - client.On("Device", mock.Anything, DeviceIDResultingInInternalServerError).Return(contract.Device{}, goErrors.New("unexpected error")) - client.On("Device", mock.Anything, DeviceIDForLockedResource).Return(contract.Device{Id: DeviceIDForLockedResource, AdminState: "LOCKED"}, nil) - client.On("Device", mock.Anything, DeviceIDd200c404).Return(contract.Device{Id: DeviceIDWithAssociatedInvalidObjectID}, nil) - client.On("Device", mock.Anything, DeviceIDd200c500).Return(contract.Device{Id: DeviceIDResultingInInternalServerError}, nil) - client.On("Device", mock.Anything, MismatchedDeviceID).Return(contract.Device{Id: MismatchedDeviceID}, nil) - client.On("Device", mock.Anything, TestDeviceID).Return(contract.Device{Id: TestDeviceID}, nil) - client.On("Device", mock.Anything, DeviceIDd200c200).Return(contract.Device{Id: DeviceIDd200c200}, nil) - return &client -} - -func newMockDBClient() interfaces.DBClient { - dbMock := &mocks.DBClient{} - dbMock.On("GetCommandsByDeviceId", DeviceIDWithAssociatedInvalidObjectID).Return(nil, db.ErrNotFound) - dbMock.On("GetCommandsByDeviceId", DeviceIDResultingInInternalServerError).Return(nil, goErrors.New("unexpected error")) - dbMock.On("GetCommandsByDeviceId", MismatchedDeviceID).Return([]models.Command{{Id: "dummy"}}, nil) - dbMock.On("GetCommandsByDeviceId", TestDeviceID).Return([]models.Command{{Id: TestCommandID}}, nil) - dbMock.On("GetCommandsByDeviceId", ExistingDeviceID).Return([]models.Command{{Id: ExistingDeviceID}}, nil) - dbMock.On("GetCommandsByDeviceId", DeviceIDd200c200).Return([]models.Command{{Id: ExistingDeviceID}}, nil) - return dbMock -} diff --git a/internal/core/command/errors/types.go b/internal/core/command/errors/types.go deleted file mode 100644 index f16be116ca..0000000000 --- a/internal/core/command/errors/types.go +++ /dev/null @@ -1,61 +0,0 @@ -package errors - -import "fmt" - -type ErrDeviceLocked struct { - device string -} - -func (e ErrDeviceLocked) Error() string { - return fmt.Sprintf("device '%s' is in admin locked state", e.device) -} - -func NewErrDeviceLocked(name string) error { - return ErrDeviceLocked{device: name} -} - -type ErrCommandNotAssociatedWithDevice struct { - commandID string - deviceID string -} - -func (e ErrCommandNotAssociatedWithDevice) Error() string { - return fmt.Sprintf("Command with id '%v' does not belong to device with id '%v'.", e.commandID, e.deviceID) -} - -func NewErrCommandNotAssociatedWithDevice(commandID string, deviceID string) error { - return ErrCommandNotAssociatedWithDevice{commandID, deviceID} -} - -// ErrExtractingInfoFromRequest is a struct that serves as the value -// receiver for Error as defined for NewErrExtractingInfoFromRequest -type ErrExtractingInfoFromRequest struct { -} - -// Error returns a meaningful string message describing error details. -func (e ErrExtractingInfoFromRequest) Error() string { - return fmt.Sprintf("error extracting command id and device id.") -} - -// NewErrExtractingInfoFromRequest returns the relevant, properly- -// constructed error type. -func NewErrExtractingInfoFromRequest() error { - return ErrExtractingInfoFromRequest{} -} - -// ErrBadRequest is a struct that serves as the value receiver -// for Error as defined for NewErrParsingOriginalRequest -type ErrBadRequest struct { - value string -} - -// Error returns a meaningful string message describing error details. -func (e ErrBadRequest) Error() string { - return fmt.Sprintf("error in parsing related to '%s'", e.value) -} - -// NewErrParsingOriginalRequest returns the relevant, properly- -// constructed error type. -func NewErrParsingOriginalRequest(invalid string) error { - return ErrBadRequest{value: invalid} -} diff --git a/internal/core/command/get.go b/internal/core/command/get.go deleted file mode 100644 index 6530b7c63d..0000000000 --- a/internal/core/command/get.go +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 command - -import ( - "context" - "net/http" - "net/url" - "strings" - - "github.com/edgexfoundry/edgex-go/internal" - "github.com/edgexfoundry/edgex-go/internal/core/command/errors" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -// NewGetCommand creates and Executor which can be used to execute the GET related command. -func NewGetCommand( - device contract.Device, - command contract.Command, - context context.Context, - httpCaller internal.HttpCaller, - lc logger.LoggingClient, - originalRequest *http.Request) (Executor, error) { - - queryParams := originalRequest.URL.RawQuery - urlResult := device.Service.Addressable.GetBaseURL() + strings.Replace( - command.Get.Action.Path, - DEVICEIDURLPARAM, - device.Id, - -1) + "?" + queryParams - validURL, err := url.ParseRequestURI(urlResult) - if err != nil { - return serviceCommand{}, err - } - deviceServiceProxiedRequest, err := http.NewRequest(http.MethodGet, validURL.String(), nil) - if err != nil { - return serviceCommand{}, err - } - - err = addHeadersToRequest(originalRequest, deviceServiceProxiedRequest, context) - if err != nil { - return serviceCommand{}, errors.NewErrParsingOriginalRequest("header") - } - - return newServiceCommand(device, httpCaller, deviceServiceProxiedRequest, lc), nil -} diff --git a/internal/core/command/get_test.go b/internal/core/command/get_test.go deleted file mode 100644 index 2a1eaa82a9..0000000000 --- a/internal/core/command/get_test.go +++ /dev/null @@ -1,183 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 command - -import ( - "context" - "net/http" - "reflect" - "strconv" - "testing" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -const ( - TestProtocol = "http" - TestDeviceId = "TestDeviceID" - TestAddress = "example.com" - TestPort = 8080 - NonPropagatedHeader = "NonPropagatedHeader" -) - -// Device which can be used as a basis for test setup. By default this is constructed for happy path testing. -var testDevice = contract.Device{ - Id: TestDeviceId, - AdminState: contract.Unlocked, - Service: contract.DeviceService{ - Addressable: contract.Addressable{ - Protocol: TestProtocol, - Address: TestAddress, - Port: TestPort, - }, - }, -} - -// Command which can be used as a basis for test setup. By default this is constructed for happy path testing. -var testCommand = contract.Command{ - Get: contract.Get{ - Action: contract.Action{ - Path: "/some/uri", - }, - }, - Put: contract.Put{ - Action: contract.Action{ - Path: "/another/uri", - }, - }, -} - -func TestNewGetCommandWithCorrelationId(t *testing.T) { - expectedCorrelationIDHeaderValue := "Testing" - req := newRequestWithHeaders(map[string]string{clients.ContentType: clients.ContentTypeCBOR}, http.MethodGet) - testContext := context.WithValue(context.Background(), clients.CorrelationHeader, expectedCorrelationIDHeaderValue) - getCommand, _ := NewGetCommand(testDevice, testCommand, testContext, nil, logger.NewMockClient(), req) - actualCorrelationIDHeaderValue := getCommand.(serviceCommand).Request.Header.Get(clients.CorrelationHeader) - - if actualCorrelationIDHeaderValue == "" { - t.Errorf("The populated GetCommand's request should contain a correlation ID header value") - } - - if actualCorrelationIDHeaderValue != expectedCorrelationIDHeaderValue { - t.Errorf("The populated GetCommand's request should contain the correct correlation ID") - } -} - -func TestNewGetCommandWithQueryParams(t *testing.T) { - req := newRequestWithHeaders(map[string]string{clients.ContentType: clients.ContentTypeCBOR}, http.MethodGet) - queryParams := "test=value1&test2=value2" - req.URL.RawQuery = queryParams - getCommand, _ := NewGetCommand(testDevice, testCommand, context.Background(), nil, logger.NewMockClient(), req) - r := getCommand.(serviceCommand).Request.URL - if r.Scheme != TestProtocol { - t.Errorf("Unexpected protocol") - } - expectedHost := TestAddress + ":" + strconv.Itoa(TestPort) - if r.Host != expectedHost { - t.Errorf("Unexpected host address and port") - } - if r.Path != testCommand.Get.Action.Path { - t.Errorf("Unexpected path") - } - if r.RawQuery != queryParams { - t.Errorf("Unexpected Raw Query Value") - } -} -func TestNewGetCommandWithMalformedQueryParams(t *testing.T) { - req := newRequestWithHeaders(map[string]string{clients.ContentType: clients.ContentTypeJSON}, http.MethodGet) - queryParams := "!@#$%" - req.URL.RawQuery = queryParams - _, err := NewGetCommand(testDevice, testCommand, context.Background(), nil, logger.NewMockClient(), req) - if err == nil { - t.Errorf("Expected error for malformed query parameters") - } -} -func TestNewGetCommandNoCorrelationIDInContext(t *testing.T) { - req := newRequestWithHeaders(map[string]string{clients.ContentType: clients.ContentTypeJSON}, http.MethodGet) - getCommand, _ := NewGetCommand(testDevice, testCommand, context.Background(), nil, logger.NewMockClient(), req) - actualCorrelationIDHeaderValue := getCommand.(serviceCommand).Request.Header.Get(clients.CorrelationHeader) - if actualCorrelationIDHeaderValue != "" { - t.Errorf("No correlation ID should be specified") - } -} - -func TestNewGetCommandInvalidBaseUrl(t *testing.T) { - device := testDevice - req := newRequestWithHeaders(map[string]string{clients.ContentType: clients.ContentTypeCBOR}, http.MethodGet) - device.Service.Addressable.Address = "!@#$" - _, err := NewGetCommand(device, testCommand, context.Background(), nil, logger.NewMockClient(), req) - if err == nil { - t.Errorf("The invalid URL error was not properly propagated to the caller") - } -} - -func TestNewGetCommandContentType(t *testing.T) { - tests := []struct { - name string - originalHeaders map[string]string - expectedHeaders map[string]string - }{ - { - name: "cbor content type header propagated", - originalHeaders: map[string]string{clients.ContentType: clients.ContentTypeCBOR}, - expectedHeaders: map[string]string{clients.ContentType: clients.ContentTypeCBOR}, - }, - { - name: "json content type header propagated", - originalHeaders: map[string]string{clients.ContentType: clients.ContentTypeJSON}, - expectedHeaders: map[string]string{clients.ContentType: clients.ContentTypeJSON}, - }, - { - name: "no content type header provided", - originalHeaders: map[string]string{clients.ContentType: ""}, - expectedHeaders: map[string]string{}, - }, - { - name: "cbor content type propagated, random header not propagated", - originalHeaders: map[string]string{clients.ContentType: clients.ContentTypeCBOR, NonPropagatedHeader: "NonPropagatedHeader"}, - expectedHeaders: map[string]string{clients.ContentType: clients.ContentTypeCBOR}, - }, - { - name: "json content type propagated, random header not propagated", - originalHeaders: map[string]string{clients.ContentType: clients.ContentTypeJSON, NonPropagatedHeader: "NonPropagatedHeader"}, - expectedHeaders: map[string]string{clients.ContentType: clients.ContentTypeJSON}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var loggerMock = logger.NewMockClient() - ctx := context.Background() - proxiedRequest := newRequestWithHeaders(tt.originalHeaders, http.MethodGet) - getCommand, _ := NewGetCommand( - testDevice, - testCommand, - ctx, - nil, - loggerMock, - proxiedRequest) - actualHeaders := map[string]string{} - for headerName, headerValues := range getCommand.(serviceCommand).Request.Header { - // Extract the first element only from slice. - actualHeaders[headerName] = headerValues[0] - } - if !reflect.DeepEqual(actualHeaders, tt.expectedHeaders) { - t.Errorf("expected %s does not match the observed %s", tt.expectedHeaders, actualHeaders) - } - }) - } -} diff --git a/internal/core/command/init.go b/internal/core/command/init.go index 6ae74ccd48..ba9e14ee31 100644 --- a/internal/core/command/init.go +++ b/internal/core/command/init.go @@ -20,19 +20,9 @@ import ( "context" "sync" - "github.com/edgexfoundry/edgex-go/internal/core/command/container" "github.com/edgexfoundry/edgex-go/internal/core/command/v2" - errorContainer "github.com/edgexfoundry/edgex-go/internal/pkg/container" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/container" "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/startup" "github.com/edgexfoundry/go-mod-bootstrap/v2/di" - V2Container "github.com/edgexfoundry/go-mod-bootstrap/v2/v2/bootstrap/container" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/metadata" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/urlclient/local" - V2Routes "github.com/edgexfoundry/go-mod-core-contracts/v2/v2" - V2Clients "github.com/edgexfoundry/go-mod-core-contracts/v2/v2/clients/http" "github.com/gorilla/mux" ) @@ -50,40 +40,7 @@ func NewBootstrap(router *mux.Router) *Bootstrap { // BootstrapHandler fulfills the BootstrapHandler contract and performs initialization needed by the command service. func (b *Bootstrap) BootstrapHandler(ctx context.Context, wg *sync.WaitGroup, _ startup.Timer, dic *di.Container) bool { - loadRestRoutes(b.router, dic) v2.LoadRestRoutes(b.router, dic) - // TODO: there is an outstanding known issue (https://github.com/edgexfoundry/edgex-go/issues/2462) - // that could be seemingly be solved by moving from JIT initialization of these external clients to static - // init on startup, like registryClient and configuration are initialized. - // Doing so would cover over the symptoms of the bug, but the root problem of server processing taking longer - // than the configured client time out would still be present. - // Until that problem is addressed by larger architectural changes, if you are experiencing a bug similar to - // https://github.com/edgexfoundry/edgex-go/issues/2421, the correct fix is to bump up the client timeout. - configuration := container.ConfigurationFrom(dic.Get) - lc := bootstrapContainer.LoggingClientFrom(dic.Get) - - // initialize clients required by the service - dic.Update(di.ServiceConstructorMap{ - container.MetadataDeviceClientName: func(get di.Get) interface{} { - return metadata.NewDeviceClient(local.New(configuration.Clients[clients.CoreMetaDataServiceKey].Url() + clients.ApiDeviceRoute)) - }, - errorContainer.ErrorHandlerName: func(get di.Get) interface{} { - return errorconcept.NewErrorHandler(lc) - }, - V2Container.MetadataDeviceClientName: func(get di.Get) interface{} { // add v2 API MetadataDeviceClient - return V2Clients.NewDeviceClient(configuration.Clients[clients.CoreMetaDataServiceKey].Url() + V2Routes.ApiDeviceRoute) - }, - V2Container.MetadataDeviceProfileClientName: func(get di.Get) interface{} { // add v2 API MetadataDeviceProfileClient - return V2Clients.NewDeviceProfileClient(configuration.Clients[clients.CoreMetaDataServiceKey].Url() + V2Routes.ApiDeviceProfileRoute) - }, - V2Container.MetadataDeviceServiceClientName: func(get di.Get) interface{} { // add v2 API MetadataDeviceServiceClient - return V2Clients.NewDeviceServiceClient(configuration.Clients[clients.CoreMetaDataServiceKey].Url() + V2Routes.ApiDeviceServiceRoute) - }, - V2Container.DeviceServiceCommandClientName: func(get di.Get) interface{} { // add v2 API DeviceServiceCommandClient - return V2Clients.NewDeviceServiceCommandClient() - }, - }) - return true } diff --git a/internal/core/command/interfaces.go b/internal/core/command/interfaces.go deleted file mode 100644 index 08036034bc..0000000000 --- a/internal/core/command/interfaces.go +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 command - -import "net/http" - -// Executor interface used to execute commands -type Executor interface { - Execute() (deviceServiceResponse *http.Response, failure error) -} diff --git a/internal/core/command/interfaces/db.go b/internal/core/command/interfaces/db.go deleted file mode 100644 index 945147a611..0000000000 --- a/internal/core/command/interfaces/db.go +++ /dev/null @@ -1,14 +0,0 @@ -package interfaces - -import ( - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type DBClient interface { - CloseSession() - GetAllCommands() ([]contract.Command, error) - GetCommandById(id string) (contract.Command, error) - GetCommandsByName(id string) ([]contract.Command, error) - GetCommandsByDeviceId(id string) ([]contract.Command, error) - GetCommandByNameAndDeviceId(cname string, did string) (contract.Command, error) -} diff --git a/internal/core/command/interfaces/mocks/DBClient.go b/internal/core/command/interfaces/mocks/DBClient.go deleted file mode 100644 index 9aa6c44bad..0000000000 --- a/internal/core/command/interfaces/mocks/DBClient.go +++ /dev/null @@ -1,127 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// DBClient is an autogenerated mock type for the DBClient type -type DBClient struct { - mock.Mock -} - -// CloseSession provides a mock function with given fields: -func (_m *DBClient) CloseSession() { - _m.Called() -} - -// GetAllCommands provides a mock function with given fields: -func (_m *DBClient) GetAllCommands() ([]models.Command, error) { - ret := _m.Called() - - var r0 []models.Command - if rf, ok := ret.Get(0).(func() []models.Command); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Command) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetCommandById provides a mock function with given fields: id -func (_m *DBClient) GetCommandById(id string) (models.Command, error) { - ret := _m.Called(id) - - var r0 models.Command - if rf, ok := ret.Get(0).(func(string) models.Command); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Command) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetCommandByNameAndDeviceId provides a mock function with given fields: cname, did -func (_m *DBClient) GetCommandByNameAndDeviceId(cname string, did string) (models.Command, error) { - ret := _m.Called(cname, did) - - var r0 models.Command - if rf, ok := ret.Get(0).(func(string, string) models.Command); ok { - r0 = rf(cname, did) - } else { - r0 = ret.Get(0).(models.Command) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(cname, did) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetCommandsByDeviceId provides a mock function with given fields: id -func (_m *DBClient) GetCommandsByDeviceId(id string) ([]models.Command, error) { - ret := _m.Called(id) - - var r0 []models.Command - if rf, ok := ret.Get(0).(func(string) []models.Command); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Command) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetCommandsByName provides a mock function with given fields: id -func (_m *DBClient) GetCommandsByName(id string) ([]models.Command, error) { - ret := _m.Called(id) - - var r0 []models.Command - if rf, ok := ret.Get(0).(func(string) []models.Command); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Command) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/command/main.go b/internal/core/command/main.go index 721c6a50b0..94198deab2 100644 --- a/internal/core/command/main.go +++ b/internal/core/command/main.go @@ -22,7 +22,6 @@ import ( "github.com/edgexfoundry/edgex-go/internal" "github.com/edgexfoundry/edgex-go/internal/core/command/config" "github.com/edgexfoundry/edgex-go/internal/core/command/container" - "github.com/edgexfoundry/edgex-go/internal/pkg/bootstrap/handlers/database" "github.com/edgexfoundry/edgex-go/internal/pkg/telemetry" "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap" @@ -37,7 +36,7 @@ import ( "github.com/gorilla/mux" ) -func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router, readyStream chan<- bool) { +func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router) { startupTimer := startup.NewStartUpTimer(clients.CoreCommandServiceKey) // All common command-line flags have been moved to DefaultCommonFlags. Service specific flags can be add here, @@ -70,12 +69,10 @@ func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router, re dic, true, []interfaces.BootstrapHandler{ - database.NewDatabase(httpServer, configuration).BootstrapHandler, NewBootstrap(router).BootstrapHandler, telemetry.BootstrapHandler, httpServer.BootstrapHandler, handlers.NewStartMessage(clients.CoreCommandServiceKey, edgex.Version).BootstrapHandler, - handlers.NewReady(httpServer, readyStream).BootstrapHandler, }) // code here! diff --git a/internal/core/command/put.go b/internal/core/command/put.go deleted file mode 100644 index 25c2bdf37b..0000000000 --- a/internal/core/command/put.go +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 command - -import ( - "context" - "net/http" - "strings" - - "github.com/edgexfoundry/edgex-go/internal" - "github.com/edgexfoundry/edgex-go/internal/core/command/errors" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -// NewPutCommand creates and Executor which can be used to execute the PUT related command. -func NewPutCommand( - device contract.Device, - command contract.Command, - body string, - context context.Context, - httpCaller internal.HttpCaller, - lc logger.LoggingClient, - originalRequest *http.Request) (Executor, error) { - - queryParams := originalRequest.URL.RawQuery - url := device.Service.Addressable.GetBaseURL() + strings.Replace( - command.Put.Action.Path, - DEVICEIDURLPARAM, - device.Id, - -1) + "?" + queryParams - deviceServiceProxiedRequest, err := http.NewRequest(http.MethodPut, url, strings.NewReader(body)) - if err != nil { - return serviceCommand{}, err - } - - err = addHeadersToRequest(originalRequest, deviceServiceProxiedRequest, context) - if err != nil { - return serviceCommand{}, errors.NewErrParsingOriginalRequest("header") - } - - return newServiceCommand(device, httpCaller, deviceServiceProxiedRequest, lc), nil -} diff --git a/internal/core/command/put_test.go b/internal/core/command/put_test.go deleted file mode 100644 index 495202fb19..0000000000 --- a/internal/core/command/put_test.go +++ /dev/null @@ -1,140 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 command - -import ( - "context" - "io/ioutil" - "net/http" - "reflect" - "testing" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" -) - -func TestNewPutCommandWithCorrelationId(t *testing.T) { - expectedCorrelationIDHeaderValue := "Testing" - testContext := context.WithValue(context.Background(), clients.CorrelationHeader, expectedCorrelationIDHeaderValue) - req := newRequestWithHeaders(map[string]string{clients.ContentType: clients.ContentTypeCBOR}, http.MethodPut) - putCommand, _ := NewPutCommand(testDevice, testCommand, "Test body", testContext, nil, logger.NewMockClient(), req) - actualCorrelationIDHeaderValue := putCommand.(serviceCommand).Request.Header.Get(clients.CorrelationHeader) - if actualCorrelationIDHeaderValue == "" { - t.Errorf("The populated PutCommand's request should contain a correlation ID header value") - } - - if actualCorrelationIDHeaderValue != expectedCorrelationIDHeaderValue { - t.Errorf("The populated PutCommand's request should contain the correct correlation ID") - } -} - -func TestNewPutCommandNoCorrelationIDInContext(t *testing.T) { - req := newRequestWithHeaders(map[string]string{clients.ContentType: clients.ContentTypeJSON}, http.MethodPut) - putCommand, _ := NewPutCommand(testDevice, testCommand, "Test Body", context.Background(), nil, logger.NewMockClient(), req) - actualCorrelationIDHeaderValue := putCommand.(serviceCommand).Request.Header.Get(clients.CorrelationHeader) - if actualCorrelationIDHeaderValue != "" { - t.Errorf("No correlation ID should be specified") - } -} - -func TestNewPutCommandInvalidBaseUrl(t *testing.T) { - device := testDevice - device.Service.Addressable.Address = "!@#$" - - req := newRequestWithHeaders(map[string]string{clients.ContentType: clients.ContentTypeCBOR}, http.MethodPut) - _, err := NewPutCommand(device, testCommand, "Test body", context.Background(), nil, logger.NewMockClient(), req) - if err != nil { - t.Errorf("The invalid URL error was not properly propagated to the caller") - } -} - -func TestNewPutCommandBody(t *testing.T) { - expectedRequestBody := "Test Request Body" - req := newRequestWithHeaders(map[string]string{clients.ContentType: clients.ContentTypeJSON}, http.MethodPut) - putCommand, err := NewPutCommand(testDevice, testCommand, expectedRequestBody, context.Background(), nil, logger.NewMockClient(), req) - - if err != nil { - t.Errorf("Unexpectedly failed while creating a PutCommand") - } - - expectedRequestBodySize := len(expectedRequestBody) - actualBodyBytes, _ := ioutil.ReadAll(putCommand.(serviceCommand).Body) - if expectedRequestBodySize != len(actualBodyBytes) { - t.Errorf("Failed to verify the request body size") - } - - actualRequestBody := string(actualBodyBytes) - if expectedRequestBody != actualRequestBody { - t.Error("Failed to verify the request body contents") - } -} - -func TestNewPutCommandContentType(t *testing.T) { - tests := []struct { - name string - originalHeaders map[string]string - expectedHeaders map[string]string - }{ - { - name: "cbor content type header propagated", - originalHeaders: map[string]string{clients.ContentType: clients.ContentTypeCBOR}, - expectedHeaders: map[string]string{clients.ContentType: clients.ContentTypeCBOR}, - }, - { - name: "json content type header propagated", - originalHeaders: map[string]string{clients.ContentType: clients.ContentTypeJSON}, - expectedHeaders: map[string]string{clients.ContentType: clients.ContentTypeJSON}, - }, - { - name: "no content type header provided", - originalHeaders: map[string]string{clients.ContentType: ""}, - expectedHeaders: map[string]string{}, - }, - { - name: "cbor content type propagated, random header not propagated", - originalHeaders: map[string]string{clients.ContentType: clients.ContentTypeCBOR, NonPropagatedHeader: "NonPropagatedHeader"}, - expectedHeaders: map[string]string{clients.ContentType: clients.ContentTypeCBOR}, - }, - { - name: "json content type propagated, random header not propagated", - originalHeaders: map[string]string{clients.ContentType: clients.ContentTypeJSON, NonPropagatedHeader: "NonPropagatedHeader"}, - expectedHeaders: map[string]string{clients.ContentType: clients.ContentTypeJSON}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var loggerMock = logger.NewMockClient() - ctx := context.Background() - proxiedRequest := newRequestWithHeaders(tt.originalHeaders, http.MethodGet) - putCommand, _ := NewPutCommand( - testDevice, - testCommand, - "Test Body", - ctx, - nil, - loggerMock, - proxiedRequest) - actualHeaders := map[string]string{} - for headerName, headerValues := range putCommand.(serviceCommand).Request.Header { - // Extract the first element only from slice. - actualHeaders[headerName] = headerValues[0] - } - if !reflect.DeepEqual(actualHeaders, tt.expectedHeaders) { - t.Errorf("expected %s does not match the observed %s", tt.expectedHeaders, actualHeaders) - } - }) - } -} diff --git a/internal/core/command/restDevice_test.go b/internal/core/command/restDevice_test.go deleted file mode 100644 index 16fbe61d2c..0000000000 --- a/internal/core/command/restDevice_test.go +++ /dev/null @@ -1,432 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 command - -import ( - "bytes" - "encoding/json" - "io/ioutil" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/edgexfoundry/edgex-go/internal" - "github.com/edgexfoundry/edgex-go/internal/core/command/config" - "github.com/edgexfoundry/edgex-go/internal/core/command/interfaces" - commandMocks "github.com/edgexfoundry/edgex-go/internal/core/command/interfaces/mocks" - "github.com/edgexfoundry/edgex-go/internal/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" -) - -var knownDeviceName = "Test Device" -var unknownDeviceName = "?" -var cmdURI = clients.ApiBase + "/" + DEVICE -var deviceId = "b3445cc6-87df-48f4-b8b0-587dc8a4e1c2" -var deviceName = "" -var TestCommandId = "TestCommandID" -var testTimestamps = models.Timestamps{Created: 123, Modified: 123, Origin: 123} -var exampleCommand = models.Command{ - Timestamps: testTimestamps, - Id: TestCommandId, - Name: "testName", -} - -var unlockedDevice = models.Device{ - Id: deviceId, - Name: deviceName, - AdminState: models.Unlocked, - Service: models.DeviceService{ - Addressable: models.Addressable{Protocol: "http", Address: "localhost", - Port: 8080}}} -var lockedDevice = models.Device{ - Id: deviceId, - Name: NAME, - AdminState: models.Locked, - Service: models.DeviceService{ - Addressable: models.Addressable{Protocol: "http", Address: "localhost", - Port: 8080}}} - -type mockOutline struct { - methodName string - arg []interface{} - ret []interface{} -} - -func TestRestGetCommandsByDeviceName(t *testing.T) { - tests := []struct { - name string - dcMock *mocks.DeviceClient - dbMock interfaces.DBClient - request *http.Request - expectedErr error - expectedStatus int - }{ - { - "ok json content type in header", - createMockUnlockedDeviceCommandClient(deviceId), - createMockWithOutlines([]mockOutline{ - {"GetCommandsByName", []interface{}{knownDeviceName}, []interface{}{nil, nil}}, - {"GetCommandsByDeviceId", []interface{}{deviceId}, []interface{}{[]models.Command{exampleCommand}, nil}}, - }), - createRequestWithPathParameters( - http.MethodGet, - clients.ContentTypeJSON, - map[string]string{ID: deviceId, COMMANDID: TestCommandId}, - createTestDeviceWithPathUrl(TestCommandId, deviceId), - exampleCommand), - nil, - http.StatusOK, - }, - { - "ok cbor content type in header", - createMockUnlockedDeviceCommandClient(deviceId), - createMockWithOutlines([]mockOutline{ - {"GetCommandsByName", []interface{}{knownDeviceName}, []interface{}{nil, nil}}, - {"GetCommandsByDeviceId", []interface{}{deviceId}, []interface{}{[]models.Command{exampleCommand}, nil}}, - }), - createRequestWithPathParameters( - http.MethodGet, - clients.ContentTypeCBOR, - map[string]string{ID: deviceId, COMMANDID: TestCommandId}, - createTestDeviceWithPathUrl(TestCommandId, deviceId), - exampleCommand), - nil, - http.StatusOK, - }, - { - "no device for requested name", - createMockUnlockedDeviceCommandClient(deviceId), - createMockWithOutlines([]mockOutline{ - {"GetCommandsByName", []interface{}{unknownDeviceName}, []interface{}{models.DeviceService{}, nil}}, - {"GetCommandsByDeviceId", []interface{}{deviceId}, []interface{}{[]models.Command{}, db.ErrNotFound}}, - }), - createGetCommandRequest(), - nil, - http.StatusNotFound, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - configuration := config.ConfigurationStruct{ - Service: bootstrapConfig.ServiceInfo{MaxResultCount: 1}, - } - restGetCommandsByDeviceName( - rr, - tt.request, - tt.dbMock, - tt.dcMock, - &configuration, - errorconcept.NewErrorHandler(loggerMock)) - response := rr.Result() - require.Equal(t, tt.expectedStatus, response.StatusCode) - }) - } -} - -func TestRestPutDeviceCommandByCommandID(t *testing.T) { - tests := []struct { - name string - dcMock *mocks.DeviceClient - dbMock interfaces.DBClient - request *http.Request - expectedErr error - expectedStatus int - }{ - { - "ok json content type in header", - createMockUnlockedDeviceCommandClient(deviceId), - createMockWithOutlines([]mockOutline{ - {"GetCommandsByName", []interface{}{knownDeviceName}, []interface{}{nil, nil}}, - {"GetCommandsByDeviceId", []interface{}{deviceId}, []interface{}{[]models.Command{exampleCommand}, nil}}, - }), - createRequestWithPathParameters( - http.MethodPut, - clients.ContentTypeJSON, - map[string]string{ID: deviceId, COMMANDID: TestCommandId}, - createTestDeviceWithPathUrl(TestCommandId, deviceId), - exampleCommand), - nil, - http.StatusOK, - }, - { - "ok cbor content type in header", - createMockUnlockedDeviceCommandClient(deviceId), - createMockWithOutlines([]mockOutline{ - {"GetCommandsByName", []interface{}{knownDeviceName}, []interface{}{nil, nil}}, - {"GetCommandsByDeviceId", []interface{}{deviceId}, []interface{}{[]models.Command{exampleCommand}, nil}}, - }), - createRequestWithPathParameters( - http.MethodPut, - clients.ContentTypeCBOR, - map[string]string{ID: deviceId, COMMANDID: TestCommandId}, - createTestDeviceWithPathUrl(TestCommandId, deviceId), - exampleCommand), - nil, - http.StatusOK, - }, - - { - "device was locked with json content type in header", - createMockLockedDeviceCommandClient(deviceId), - createMockWithOutlines([]mockOutline{ - {"GetCommandsByName", []interface{}{knownDeviceName}, []interface{}{nil, nil}}, - {"GetCommandsByDeviceId", []interface{}{deviceId}, []interface{}{[]models.Command{exampleCommand}, nil}}, - }), - createRequestWithPathParameters( - http.MethodPut, - clients.ContentTypeJSON, - map[string]string{ID: deviceId, COMMANDID: TestCommandId}, - createTestDeviceWithPathUrl(TestCommandId, deviceId), - exampleCommand), - nil, - http.StatusLocked, - }, - { - "device was locked with cbor content type in header", - createMockLockedDeviceCommandClient(deviceId), - createMockWithOutlines([]mockOutline{ - {"GetCommandsByName", []interface{}{knownDeviceName}, []interface{}{nil, nil}}, - {"GetCommandsByDeviceId", []interface{}{deviceId}, []interface{}{[]models.Command{exampleCommand}, nil}}, - }), - createRequestWithPathParameters( - http.MethodPut, - clients.ContentTypeCBOR, - map[string]string{ID: deviceId, COMMANDID: TestCommandId}, - createTestDeviceWithPathUrl(TestCommandId, deviceId), - exampleCommand), - nil, - http.StatusLocked, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - httpCaller := createMockHttpCaller() - restPutDeviceCommandByCommandID( - rr, - tt.request, - loggerMock, - tt.dbMock, - tt.dcMock, - errorconcept.NewErrorHandler(loggerMock), - httpCaller) - response := rr.Result() - require.Equal(t, tt.expectedStatus, response.StatusCode) - }) - } -} - -func TestRestGetDeviceCommandByCommandID(t *testing.T) { - tests := []struct { - name string - dcMock *mocks.DeviceClient - dbMock interfaces.DBClient - request *http.Request - expectedErr error - expectedStatus int - }{ - { - "ok json content type in header", - createMockUnlockedDeviceCommandClient(deviceId), - createMockWithOutlines([]mockOutline{ - {"GetCommandsByName", []interface{}{knownDeviceName}, []interface{}{nil, nil}}, - {"GetCommandsByDeviceId", []interface{}{deviceId}, []interface{}{[]models.Command{exampleCommand}, nil}}, - }), - createRequestWithPathParameters( - http.MethodGet, - clients.ContentTypeJSON, - map[string]string{ID: deviceId, COMMANDID: TestCommandId}, - createTestDeviceWithPathUrl(TestCommandId, deviceId), - exampleCommand), - nil, - http.StatusOK, - }, - { - "ok cbor content type in header", - createMockUnlockedDeviceCommandClient(deviceId), - createMockWithOutlines([]mockOutline{ - {"GetCommandsByName", []interface{}{knownDeviceName}, []interface{}{nil, nil}}, - {"GetCommandsByDeviceId", []interface{}{deviceId}, []interface{}{[]models.Command{exampleCommand}, nil}}, - }), - createRequestWithPathParameters( - http.MethodGet, - clients.ContentTypeCBOR, - map[string]string{ID: deviceId, COMMANDID: TestCommandId}, - createTestDeviceWithPathUrl(TestCommandId, deviceId), - exampleCommand), - nil, - http.StatusOK, - }, - { - "device was locked with json content type in header", - createMockLockedDeviceCommandClient(deviceId), - createMockWithOutlines([]mockOutline{ - {"GetCommandsByName", []interface{}{knownDeviceName}, []interface{}{nil, nil}}, - {"GetCommandsByDeviceId", []interface{}{deviceId}, []interface{}{[]models.Command{exampleCommand}, nil}}, - }), - createRequestWithPathParameters( - http.MethodGet, - clients.ContentTypeJSON, - map[string]string{ID: deviceId, COMMANDID: TestCommandId}, - createTestDeviceWithPathUrl(TestCommandId, deviceId), - exampleCommand), - nil, - http.StatusLocked, - }, - { - "device was locked with cbor content type in header", - createMockLockedDeviceCommandClient(deviceId), - createMockWithOutlines([]mockOutline{ - {"GetCommandsByName", []interface{}{knownDeviceName}, []interface{}{nil, nil}}, - {"GetCommandsByDeviceId", []interface{}{deviceId}, []interface{}{[]models.Command{exampleCommand}, nil}}, - }), - createRequestWithPathParameters( - http.MethodGet, - clients.ContentTypeCBOR, - map[string]string{ID: deviceId, COMMANDID: TestCommandId}, - createTestDeviceWithPathUrl(TestCommandId, deviceId), - exampleCommand), - nil, - http.StatusLocked, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - httpCaller := createMockHttpCaller() - restGetDeviceCommandByCommandID( - rr, - tt.request, - loggerMock, - tt.dbMock, - tt.dcMock, - errorconcept.NewErrorHandler(loggerMock), - httpCaller) - response := rr.Result() - require.Equal(t, tt.expectedStatus, response.StatusCode) - }) - } -} - -func createMockWithOutlines(outlines []mockOutline) interfaces.DBClient { - dbMock := commandMocks.DBClient{} - - for _, o := range outlines { - dbMock.On(o.methodName, o.arg...).Return(o.ret...) - } - return &dbMock -} - -// An HttpCaller which returns a normal response when a mocked request is executed. -type NormalMockHttpCaller struct{} - -// MockNormalBody is a body that will return... -type MockNormalBody struct{} - -// Read returns ... -func (MockNormalBody) Read(p []byte) (n int, err error) { - return 0, nil -} -func (MockNormalBody) ReadFrom(p []byte) (n int, err error) { - return 0, nil -} - -// Close implementation is not required -func (MockNormalBody) Close() error { - panic("implement me") -} - -func (NormalMockHttpCaller) Do(req *http.Request) (*http.Response, error) { - return &http.Response{ - StatusCode: http.StatusOK, - Body: MockNormalBody{}, - }, nil -} - -func createMockHttpCaller() internal.HttpCaller { - reqBody := ioutil.NopCloser(bytes.NewReader([]byte(exampleCommand.String()))) - resp := &http.Response{ - StatusCode: http.StatusOK, - Body: reqBody, - } - - req := &mocks.HttpCaller{} - req.On("Do", mock.Anything).Return(resp, nil) - return req -} - -func createMockUnlockedDeviceCommandClient(deviceId string) *mocks.DeviceClient { - client := &mocks.DeviceClient{} - client.On("DeviceForName", mock.Anything, knownDeviceName).Return(unlockedDevice, nil) - client.On("DeviceForName", mock.Anything, unknownDeviceName).Return(unlockedDevice, db.ErrNotFound) - client.On("DeviceForName", mock.Anything, "").Return(unlockedDevice, nil) - client.On("Device", mock.Anything, deviceId).Return(unlockedDevice, nil) - client.On("Device", mock.Anything, "").Return(unlockedDevice, nil) - client.On("Device", mock.Anything, "").Return(unlockedDevice, nil) - return client -} - -func createMockLockedDeviceCommandClient(deviceId string) *mocks.DeviceClient { - client := &mocks.DeviceClient{} - client.On("DeviceForName", mock.Anything, knownDeviceName).Return(lockedDevice, nil) - client.On("DeviceForName", mock.Anything, unknownDeviceName).Return(lockedDevice, db.ErrNotFound) - client.On("DeviceForName", mock.Anything, "").Return(lockedDevice, nil) - client.On("Device", mock.Anything, deviceId).Return(lockedDevice, nil) - client.On("Device", mock.Anything, "").Return(lockedDevice, nil) - client.On("Device", mock.Anything, "").Return(lockedDevice, nil) - return client -} - -func createGetCommandRequest() *http.Request { - return httptest.NewRequest(http.MethodGet, cmdURI, nil) -} - -func createRequestWithPathParameters( - httpMethod string, - contentType string, - params map[string]string, - sampleDevice models.Device, - cmd models.Command) *http.Request { - - cmdBody, _ := json.Marshal(cmd) - req, _ := http.NewRequest(httpMethod, cmdURI, strings.NewReader(string(cmdBody))) - req.Header.Set(clients.ContentType, contentType) - req.URL.Path = sampleDevice.Service.Addressable.Path - req.Context() - - return mux.SetURLVars(req, params) -} diff --git a/internal/core/command/rest_device.go b/internal/core/command/rest_device.go deleted file mode 100644 index bd7b9ae362..0000000000 --- a/internal/core/command/rest_device.go +++ /dev/null @@ -1,288 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Inc. - * - * 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 command - -import ( - "encoding/json" - "io/ioutil" - "net/http" - - "github.com/edgexfoundry/edgex-go/internal" - "github.com/edgexfoundry/edgex-go/internal/core/command/config" - "github.com/edgexfoundry/edgex-go/internal/core/command/interfaces" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/metadata" - - "github.com/gorilla/mux" -) - -func restGetDeviceCommandByCommandID( - w http.ResponseWriter, - originalRequest *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - deviceClient metadata.DeviceClient, - httpErrorHandler errorconcept.ErrorHandler, - httpCaller internal.HttpCaller) { - - issueDeviceCommand(w, originalRequest, lc, dbClient, deviceClient, httpErrorHandler, httpCaller) -} - -func restPutDeviceCommandByCommandID( - w http.ResponseWriter, - originalRequest *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - deviceClient metadata.DeviceClient, - httpErrorHandler errorconcept.ErrorHandler, - httpCaller internal.HttpCaller) { - - issueDeviceCommand(w, originalRequest, lc, dbClient, deviceClient, httpErrorHandler, httpCaller) -} - -func issueDeviceCommand( - w http.ResponseWriter, - originalRequest *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - deviceClient metadata.DeviceClient, - httpErrorHandler errorconcept.ErrorHandler, - httpCaller internal.HttpCaller) { - - defer originalRequest.Body.Close() - - b, err := ioutil.ReadAll(originalRequest.Body) - if b == nil && err != nil { - lc.Error(err.Error()) - return - } - - deviceServiceResponse, deviceServiceResponseBody, err := executeCommandByDeviceID( - originalRequest, - string(b), - lc, - dbClient, - deviceClient, - httpCaller) - - if err != nil { - httpErrorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.NewServiceClientHttpError(err), - errorconcept.Device.Locked, - errorconcept.Database.NotFound, - errorconcept.Command.NotAssociatedWithDevice, - }, - errorconcept.Default.InternalServerError) - return - } - - // Extract the headers from the device service's response, so - // we can propagate it to functions upward in the call chain. - headers := map[string]string{"Content-Type": ""} - m := make(map[string][]string) - m = deviceServiceResponse.Header - for name, values := range m { - for _, value := range values { - headers[name] = value - } - } - - // Set the returned header Content-type based on header Content-type received in - // the Device Service request (No need to inspect it). - w.Header().Set(clients.ContentType, headers[clients.ContentType]) - w.WriteHeader(http.StatusOK) - w.Write([]byte(deviceServiceResponseBody)) -} - -func restGetDeviceCommandByNames( - w http.ResponseWriter, - originalRequest *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - deviceClient metadata.DeviceClient, - httpErrorHandler errorconcept.ErrorHandler, - httpCaller internal.HttpCaller) { - - issueDeviceCommandByNames(w, originalRequest, lc, dbClient, deviceClient, httpErrorHandler, httpCaller) -} - -func restPutDeviceCommandByNames( - w http.ResponseWriter, - originalRequest *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - deviceClient metadata.DeviceClient, - httpErrorHandler errorconcept.ErrorHandler, - httpCaller internal.HttpCaller) { - - issueDeviceCommandByNames(w, originalRequest, lc, dbClient, deviceClient, httpErrorHandler, httpCaller) -} - -func issueDeviceCommandByNames( - w http.ResponseWriter, - originalRequest *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - deviceClient metadata.DeviceClient, - httpErrorHandler errorconcept.ErrorHandler, - httpCaller internal.HttpCaller) { - - defer originalRequest.Body.Close() - - vars := mux.Vars(originalRequest) - dn := vars[NAME] - cn := vars[COMMANDNAME] - - ctx := originalRequest.Context() - - b, err := ioutil.ReadAll(originalRequest.Body) - if b == nil && err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - deviceServiceResponse, deviceServiceResponseBody, err := executeCommandByName( - originalRequest, - ctx, - dn, - cn, - string(b), - lc, - dbClient, - deviceClient, - httpCaller) - - if err != nil { - httpErrorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.NewServiceClientHttpError(err), - errorconcept.Device.Locked, - errorconcept.Database.NotFound, - }, - errorconcept.Default.InternalServerError) - return - } - - // Extract the headers from the device service's response, so - // we can propagate it to functions upward in the call chain. - headers := map[string]string{"Content-Type": ""} - m := make(map[string][]string) - m = deviceServiceResponse.Header - for name, values := range m { - for _, value := range values { - headers[name] = value - } - } - - // Set the returned header Content-type based on header Content-type received in - // the Device Service request (No need to inspect it). - w.Header().Set(clients.ContentType, headers[clients.ContentType]) - w.WriteHeader(http.StatusOK) - w.Write([]byte(deviceServiceResponseBody)) -} - -func restGetCommandsByDeviceID( - w http.ResponseWriter, - originalRequest *http.Request, - dbClient interfaces.DBClient, - deviceClient metadata.DeviceClient, - configuration *config.ConfigurationStruct, - httpErrorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(originalRequest) - did := vars[ID] - ctx := originalRequest.Context() - device, err := getCommandsByDeviceID(ctx, did, dbClient, deviceClient, configuration) - if err != nil { - httpErrorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.NewServiceClientHttpError(err), - errorconcept.Device.NotFoundInDB, - errorconcept.Database.NotFound, - }, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(&device) -} - -func restGetCommandsByDeviceName( - w http.ResponseWriter, - originalRequest *http.Request, - dbClient interfaces.DBClient, - deviceClient metadata.DeviceClient, - configuration *config.ConfigurationStruct, - httpErrorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(originalRequest) - dn := vars[NAME] - ctx := originalRequest.Context() - devices, err := getCommandsByDeviceName(ctx, dn, dbClient, deviceClient, configuration) - if err != nil { - httpErrorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.NewServiceClientHttpError(err), - errorconcept.Device.NotFoundInDB, - }, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(&devices) -} - -func restGetAllCommands( - w http.ResponseWriter, - originalRequest *http.Request, - dbClient interfaces.DBClient, - deviceClient metadata.DeviceClient, - configuration *config.ConfigurationStruct, - httpErrorHandler errorconcept.ErrorHandler) { - - ctx := originalRequest.Context() - devices, err := getAllCommands(ctx, dbClient, deviceClient, configuration) - if err != nil { - httpErrorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.NewServiceClientHttpError(err), - errorconcept.Database.NotFound, - }, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(devices) -} diff --git a/internal/core/command/router.go b/internal/core/command/router.go deleted file mode 100644 index a373455094..0000000000 --- a/internal/core/command/router.go +++ /dev/null @@ -1,170 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * - * 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 command - -import ( - "net/http" - - commandContainer "github.com/edgexfoundry/edgex-go/internal/core/command/container" - "github.com/edgexfoundry/edgex-go/internal/pkg" - "github.com/edgexfoundry/edgex-go/internal/pkg/bootstrap/container" - errorContainer "github.com/edgexfoundry/edgex-go/internal/pkg/container" - "github.com/edgexfoundry/edgex-go/internal/pkg/correlation" - "github.com/edgexfoundry/edgex-go/internal/pkg/telemetry" - - bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/container" - "github.com/edgexfoundry/go-mod-bootstrap/v2/di" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - - "github.com/gorilla/mux" -) - -func loadRestRoutes(r *mux.Router, dic *di.Container) { - // Ping Resource - r.HandleFunc( - clients.ApiPingRoute, - func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set(clients.ContentType, clients.ContentTypeText) - _, _ = w.Write([]byte("pong")) - }).Methods(http.MethodGet) - - // Configuration - r.HandleFunc( - clients.ApiConfigRoute, - func(w http.ResponseWriter, _ *http.Request) { - pkg.Encode(commandContainer.ConfigurationFrom(dic.Get), w, bootstrapContainer.LoggingClientFrom(dic.Get)) - }).Methods(http.MethodGet) - - // Metrics - r.HandleFunc( - clients.ApiMetricsRoute, - func(w http.ResponseWriter, _ *http.Request) { - pkg.Encode(telemetry.NewSystemUsage(), w, bootstrapContainer.LoggingClientFrom(dic.Get)) - }).Methods(http.MethodGet) - - // Version - r.HandleFunc(clients.ApiVersionRoute, pkg.VersionHandler).Methods(http.MethodGet) - - b := r.PathPrefix(clients.ApiBase).Subrouter() - - loadDeviceRoutes(b, dic) - - r.Use(correlation.ManageHeader) - r.Use(correlation.LoggingMiddleware(bootstrapContainer.LoggingClientFrom(dic.Get))) -} - -func loadDeviceRoutes(b *mux.Router, dic *di.Container) { - b.HandleFunc( - "/device", - func(w http.ResponseWriter, r *http.Request) { - restGetAllCommands( - w, - r, - container.DBClientFrom(dic.Get), - commandContainer.MetadataDeviceClientFrom(dic.Get), - commandContainer.ConfigurationFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - d := b.PathPrefix("/" + DEVICE).Subrouter() - - // /api//device - d.HandleFunc( - "/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetCommandsByDeviceID( - w, - r, - container.DBClientFrom(dic.Get), - commandContainer.MetadataDeviceClientFrom(dic.Get), - commandContainer.ConfigurationFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - d.HandleFunc( - "/{"+ID+"}/"+COMMAND+"/{"+COMMANDID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetDeviceCommandByCommandID( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - commandContainer.MetadataDeviceClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - &http.Client{}) - }).Methods(http.MethodGet) - d.HandleFunc( - "/{"+ID+"}/"+COMMAND+"/{"+COMMANDID+"}", - func(w http.ResponseWriter, r *http.Request) { - restPutDeviceCommandByCommandID( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - commandContainer.MetadataDeviceClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - &http.Client{}) - }).Methods(http.MethodPut) - // In the block of code above, as well as in the one that follows below, - // there are two references each to http.Client. Putting them into the - // DI container(dic) and retrieving the value like we do for other components - // would bring about further consistency in the code base. But the concern - // then would be the creation of a race condition because we can only have a - // single http.Client instance in the dic. In turn, every invocation of this - // REST handler would be served by a different goroutine. This would create - // a situation where each one of them would use the same http.Client instance, - // resulting in state divergence, misalignment. So the decision is to not - // put this into the DI container(dic). - - // /api//device/name - dn := d.PathPrefix("/" + NAME).Subrouter() - - dn.HandleFunc( - "/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetCommandsByDeviceName( - w, - r, - container.DBClientFrom(dic.Get), - commandContainer.MetadataDeviceClientFrom(dic.Get), - commandContainer.ConfigurationFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - ) - }).Methods(http.MethodGet) - dn.HandleFunc( - "/{"+NAME+"}/"+COMMAND+"/{"+COMMANDNAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetDeviceCommandByNames( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - commandContainer.MetadataDeviceClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - &http.Client{}) - }).Methods(http.MethodGet) - dn.HandleFunc( - "/{"+NAME+"}/"+COMMAND+"/{"+COMMANDNAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restPutDeviceCommandByNames( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - commandContainer.MetadataDeviceClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - &http.Client{}) - }).Methods(http.MethodPut) -} diff --git a/internal/core/command/test_utils.go b/internal/core/command/test_utils.go deleted file mode 100644 index 9ba4157928..0000000000 --- a/internal/core/command/test_utils.go +++ /dev/null @@ -1,18 +0,0 @@ -package command - -import ( - "net/http" - "strings" -) - -// newRequestWithHeaders accepts httpMethod and outlines, which is a map[string]string, -// and, by looping over the entries in outlines, populates the HTTP headers of the -// http.Request, which is returned from this function. -func newRequestWithHeaders(outlines map[string]string, httpMethod string) *http.Request { - req, _ := http.NewRequest(httpMethod, "/", strings.NewReader("Test body")) - for k, v := range outlines { - req.Header.Set(k, v) - } - - return req -} diff --git a/internal/core/command/types.go b/internal/core/command/types.go deleted file mode 100644 index 9f9426c364..0000000000 --- a/internal/core/command/types.go +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 command - -import ( - "net/http" - - "github.com/edgexfoundry/edgex-go/internal" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -// serviceCommand type which encapsulates command information to be sent to the command service. -type serviceCommand struct { - contract.Device - internal.HttpCaller - *http.Request - logger.LoggingClient -} - -// Execute sends the command to the core-command service and gets a deviceServiceResponse back, -// which it then returns, along with other parameters. -func (sc serviceCommand) Execute() (deviceServiceResponse *http.Response, failure error) { - - sc.LoggingClient.Debug("Issuing" + sc.Request.Method + " command to: " + sc.Request.URL.String()) - deviceServiceResponse, reqErr := sc.HttpCaller.Do(sc.Request) - if reqErr != nil { - sc.LoggingClient.Error(reqErr.Error()) - return nil, reqErr - } - - return deviceServiceResponse, nil -} - -func newServiceCommand( - device contract.Device, - caller internal.HttpCaller, - req *http.Request, - lc logger.LoggingClient) serviceCommand { - return serviceCommand{ - Device: device, - HttpCaller: caller, - Request: req, - LoggingClient: lc, - } -} diff --git a/internal/core/command/types_test.go b/internal/core/command/types_test.go deleted file mode 100644 index cdd70931f1..0000000000 --- a/internal/core/command/types_test.go +++ /dev/null @@ -1,117 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 command - -import ( - "bytes" - "errors" - "fmt" - "net/http" - "net/http/httptest" - "testing" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -// FailingMockHttpCaller a HttpCaller which always returns an error when executing a request. -type FailingMockHttpCaller struct{} - -// Do always returns an empty http.Response and an error. -func (FailingMockHttpCaller) Do(req *http.Request) (*http.Response, error) { - return &http.Response{}, errors.New("testing error handling") -} - -// ReadFailMockHttpCaller an HttpCaller which returns a http.Response that will always fail when attempting to read the Body. -type ReadFailMockHttpCaller struct{} - -// Do returns a http.Response which contains a Body that will return an error when attempting to read. -func (ReadFailMockHttpCaller) Do(req *http.Request) (*http.Response, error) { - return &http.Response{ - Body: MockBody{}, - }, errors.New("testing error") -} - -// MockBody a Body which will return 0 and an error when attempting to read. -type MockBody struct{} - -// Read returns 0 and an error -func (MockBody) Read(p []byte) (n int, err error) { - return 0, errors.New("testing read error") -} - -// Close implementation not required -func (MockBody) Close() error { - panic("implement me") -} - -func TestExecute(t *testing.T) { - expectedResponseBody := "Sample Response Body" - expectedResponseCode := http.StatusOK - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprint(w, expectedResponseBody) - w.WriteHeader(expectedResponseCode) - })) - defer ts.Close() - - request, _ := http.NewRequest(http.MethodGet, ts.URL, nil) - sc := newServiceCommand(contract.Device{AdminState: contract.Unlocked}, &http.Client{}, request, logger.NewMockClient()) - deviceServiceResponse, err := sc.Execute() - - if err != nil { - t.Errorf("No error should be present for happy path") - } - - // Extract the headers from the device service's response. - headers := map[string]string{"Content-Type": ""} - m := make(map[string][]string) - m = deviceServiceResponse.Header - for name, values := range m { - for _, value := range values { - headers[name] = value - } - } - - if headers == nil { - t.Errorf("The response body was properly propagated to the caller") - } - - // Extract the response body from the device service's response. - responseBody := new(bytes.Buffer) - _, _ = responseBody.ReadFrom(deviceServiceResponse.Body) - if responseBody.String() != expectedResponseBody { - t.Errorf("The response body was not properly propagated to the caller") - } -} - -func TestExecuteHttpRequestError(t *testing.T) { - request, _ := http.NewRequest(http.MethodGet, "http://example.com", nil) - sc := newServiceCommand(contract.Device{AdminState: contract.Unlocked}, &FailingMockHttpCaller{}, request, logger.NewMockClient()) - - _, err := sc.Execute() - if err == nil { - t.Errorf("No error should be present for happy path") - } -} - -func TestExecuteHttpReadResponseError(t *testing.T) { - request, _ := http.NewRequest(http.MethodGet, "", nil) - sc := newServiceCommand(contract.Device{AdminState: contract.Unlocked}, &ReadFailMockHttpCaller{}, request, logger.NewMockClient()) - - _, err := sc.Execute() - if err == nil { - t.Errorf("The error was not properly propagated to the caller") - } -} diff --git a/internal/core/command/utils.go b/internal/core/command/utils.go deleted file mode 100644 index cc15a3f276..0000000000 --- a/internal/core/command/utils.go +++ /dev/null @@ -1,43 +0,0 @@ -package command - -import ( - "context" - "github.com/edgexfoundry/edgex-go/internal/core/command/errors" - "net/http" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" -) - -// propagatedHeader is a slice containing request headers that we want to propagate. It is -// used by the function addHeadersToRequest, which is responsible for adding the headers -// by iterating over the slice, adding any header values contained in this slice. -var propagatedHeader = []string{clients.ContentType} - -// addHeadersToRequest populates deviceServiceRequest with matching headers from -// originalRequest to retain information that can be used by the device service. -func addHeadersToRequest( - originalRequest *http.Request, - deviceServiceProxiedRequest *http.Request, - context context.Context) error { - - if originalRequest == nil { - return errors.NewErrParsingOriginalRequest("header") - } - - for _, headerName := range propagatedHeader { - originalHeader := originalRequest.Header.Get(headerName) - if originalHeader != "" { - deviceServiceProxiedRequest.Header.Set(headerName, originalHeader) - } - } - - // Also populate deviceServiceProxiedRequest with clients.CorrelationHeader value - // from originalRequest (via the context.Value() part of the request) since - // inclusion of a Correlation ID (in deviceServiceProxiedRequest) is a requirement. - correlationID := context.Value(clients.CorrelationHeader) - if correlationID != nil { - deviceServiceProxiedRequest.Header.Set(clients.CorrelationHeader, correlationID.(string)) - } - - return nil -} diff --git a/internal/core/data/README.md b/internal/core/data/README.md index 03c1fb9236..0b5359f781 100644 --- a/internal/core/data/README.md +++ b/internal/core/data/README.md @@ -20,7 +20,7 @@ cd $GOPATH/src/github.com/edgexfoundry/edgex-go # pull the 3rd party / vendor packages make prepare # build the microservice -make cmd/core-data/core-data +make core-data # get to the core data microservice executable cd cmd/core-data # run the microservice (may require other dependent services to run correctly) diff --git a/internal/core/data/const.go b/internal/core/data/const.go deleted file mode 100644 index c3d094446a..0000000000 --- a/internal/core/data/const.go +++ /dev/null @@ -1,38 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 data - -const ( - SCRUB = "scrub" - SCRUBALL = "scruball" - COUNT = "count" - CHECKSUM = "checksum" - LABEL = "label" - DEVICEID_PARAM = "deviceId" - UOMLABEL = "uomlabel" - UOMLABEL_PARAM = "uomLabel" - LIMIT = "limit" - REMOVEOLD = "removeold" - AGE = "age" - START = "start" - END = "end" - TYPE = "type" - DEVICENAME = "devicename" - DEVICEID = "deviceid" - ID = "id" - NAME = "name" - NAMES = "names" - DEVICE = "device" - USAGE = "usage" -) diff --git a/internal/core/data/container/events.go b/internal/core/data/container/events.go deleted file mode 100644 index 4dd7126ce1..0000000000 --- a/internal/core/data/container/events.go +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************** - * Copyright 2019 Dell Inc. - * - * 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 container - -import "github.com/edgexfoundry/go-mod-bootstrap/v2/di" - -// EventsChannelName contains the name of the Events channel instance in the DIC. -const EventsChannelName = "CoreDataEventsChannel" - -// PublisherEventsChannelFrom helper function queries the DIC and returns the Events channel instance used for -// publishing over the channel. -// -// NOTE If there is a need to obtain a consuming version of the channel create a new helper function which will get the -// channel from the container and cast it to a consuming channel. The type casting will aid in avoiding errors by -// restricting functionality. -func PublisherEventsChannelFrom(get di.Get) chan<- interface{} { - return get(EventsChannelName).(chan interface{}) -} diff --git a/internal/core/data/container/metadata.go b/internal/core/data/container/metadata.go deleted file mode 100644 index b9ddd405f3..0000000000 --- a/internal/core/data/container/metadata.go +++ /dev/null @@ -1,28 +0,0 @@ -/******************************************************************************** - * Copyright 2019 Dell Inc. - * - * 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 container - -import ( - "github.com/edgexfoundry/go-mod-bootstrap/v2/di" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/metadata" -) - -// MetadataDeviceClientName contains the name of the Metadata device client instance in the DIC. -var MetadataDeviceClientName = "MetadataDeviceClient" - -// MetadataDeviceClientFrom helper function queries the DIC and returns the Metadata device client instance. -func MetadataDeviceClientFrom(get di.Get) metadata.DeviceClient { - return get(MetadataDeviceClientName).(metadata.DeviceClient) -} diff --git a/internal/core/data/device.go b/internal/core/data/device.go deleted file mode 100644 index a0380eeaef..0000000000 --- a/internal/core/data/device.go +++ /dev/null @@ -1,118 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Inc. - * - * 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 data - -import ( - "context" - - "github.com/edgexfoundry/edgex-go/internal/core/data/config" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/metadata" -) - -// Update when the device was last reported connected -func updateDeviceLastReportedConnected( - device string, - lc logger.LoggingClient, - mdc metadata.DeviceClient, - configuration *config.ConfigurationStruct) { - // Config set to skip update last reported - if !configuration.Writable.DeviceUpdateLastConnected { - lc.Debug("Skipping update of device connected/reported times for: " + device) - return - } - - d, err := mdc.CheckForDevice(context.Background(), device) - if err != nil { - lc.Error("Error getting device " + device + ": " + err.Error()) - return - } - - // Couldn't find device - if len(d.Name) == 0 { - lc.Error("Error updating device connected/reported times. Unknown device with identifier of: " + device) - return - } - - t := db.MakeTimestamp() - // Found device, now update lastReported - // Use of context.Background because this function is invoked asynchronously from a channel - err = mdc.UpdateLastConnectedByName(context.Background(), d.Name, t) - if err != nil { - lc.Error("Problems updating last connected value for device: " + d.Name) - return - } - err = mdc.UpdateLastReportedByName(context.Background(), d.Name, t) - if err != nil { - lc.Error("Problems updating last reported value for device: " + d.Name) - } - return -} - -// Update when the device service was last reported connected -func updateDeviceServiceLastReportedConnected( - device string, - lc logger.LoggingClient, - mdc metadata.DeviceClient, - msc metadata.DeviceServiceClient, - configuration *config.ConfigurationStruct) { - - if !configuration.Writable.ServiceUpdateLastConnected { - lc.Debug("Skipping update of device service connected/reported times for: " + device) - return - } - - t := db.MakeTimestamp() - - // Get the device - d, err := mdc.CheckForDevice(context.Background(), device) - if err != nil { - lc.Error("Error getting device " + device + ": " + err.Error()) - return - } - - // Couldn't find device - if len(d.Name) == 0 { - lc.Error("Error updating device connected/reported times. Unknown device with identifier of: " + device) - return - } - - // Get the device service - s := d.Service - if &s == nil { - lc.Error("Error updating device service connected/reported times. Unknown device service in device: " + d.Name) - return - } - - // Use of context.Background because this function is invoked asynchronously from a channel - _ = msc.UpdateLastConnected(context.Background(), s.Id, t) - _ = msc.UpdateLastReported(context.Background(), s.Id, t) -} - -func checkDevice( - device string, - ctx context.Context, - mdc metadata.DeviceClient, - configuration *config.ConfigurationStruct) error { - - if configuration.Writable.MetaDataCheck { - _, err := mdc.CheckForDevice(ctx, device) - if err != nil { - return err - } - } - return nil -} diff --git a/internal/core/data/device_test.go b/internal/core/data/device_test.go deleted file mode 100644 index 5ee807a9ec..0000000000 --- a/internal/core/data/device_test.go +++ /dev/null @@ -1,114 +0,0 @@ -package data - -import ( - "math" - "sync" - "testing" - "time" - - "github.com/edgexfoundry/edgex-go/internal/core/data/config" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/google/uuid" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -var testEvent contract.Event - -const ( - testDeviceName string = "Test Device" - testOrigin int64 = 123456789 - testBsonString string = "57e59a71e4b0ca8e6d6d4cc2" - testUUIDString string = "ca93c8fa-9919-4ec5-85d3-f81b2b6a7bc1" -) - -func TestCheckMaxLimit(t *testing.T) { - reset() - - testedLimit := math.MinInt32 - - expectedNil := checkMaxLimit(testedLimit, logger.NewMockClient(), &config.ConfigurationStruct{}) - - if expectedNil != nil { - t.Errorf("Should not exceed limit") - } -} - -func TestCheckMaxLimitOverLimit(t *testing.T) { - reset() - - testedLimit := math.MaxInt32 - - expectedErr := checkMaxLimit(testedLimit, logger.NewMockClient(), &config.ConfigurationStruct{}) - - if expectedErr == nil { - t.Errorf("Exceeded limit and should throw error") - } -} - -// Supporting methods -// Reset() re-initializes dependencies for each test -func reset() { - testEvent.ID = testBsonString - testEvent.Device = testDeviceName - testEvent.Origin = testOrigin - testEvent.Readings = buildReadings() -} - -func buildReadings() []contract.Reading { - ticks := db.MakeTimestamp() - - id, _ := uuid.NewUUID() - r1 := contract.Reading{Id: id.String(), - Name: "Temperature", - Value: "45", - Origin: testOrigin, - Created: ticks, - Modified: ticks, - Pushed: ticks, - Device: testDeviceName} - - id, _ = uuid.NewUUID() - r2 := contract.Reading{Id: id.String(), - Name: "Pressure", - Value: "1.01325", - Origin: testOrigin, - Created: ticks, - Modified: ticks, - Pushed: ticks, - Device: testDeviceName} - readings := []contract.Reading{} - readings = append(readings, r1, r2) - return readings -} - -func handleDomainEvents(bitEvents []bool, chEvents <-chan interface{}, wait *sync.WaitGroup, t *testing.T) { - until := time.Now().Add(500 * time.Millisecond) // Kill this loop after half second. - for time.Now().Before(until) { - select { - case evt := <-chEvents: - switch evt.(type) { - case DeviceLastReported: - e := evt.(DeviceLastReported) - if e.DeviceName != testDeviceName { - t.Errorf("DeviceLastReported name mismatch %s", e.DeviceName) - return - } - bitEvents[0] = true - break - case DeviceServiceLastReported: - e := evt.(DeviceServiceLastReported) - if e.DeviceName != testDeviceName { - t.Errorf("DeviceLastReported name mismatch %s", e.DeviceName) - return - } - bitEvents[1] = true - break - } - default: - // Without a default case in here, the select block will hang. - } - } - wait.Done() -} diff --git a/internal/core/data/domain_events.go b/internal/core/data/domain_events.go deleted file mode 100644 index f6e904bd65..0000000000 --- a/internal/core/data/domain_events.go +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Inc. - * - * 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 data - -import ( - "github.com/edgexfoundry/edgex-go/internal/core/data/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/metadata" -) - -// An event indicating that a given device has just reported some data -type DeviceLastReported struct { - DeviceName string -} - -// An event indicating that the service associated with the device that just reported data is alive. -type DeviceServiceLastReported struct { - DeviceName string -} - -func initEventHandlers( - lc logger.LoggingClient, - chEvents <-chan interface{}, - mdc metadata.DeviceClient, - msc metadata.DeviceServiceClient, - configuration *config.ConfigurationStruct) { - go func() { - for { - select { - case e, ok := <-chEvents: - if ok { - switch e.(type) { - case DeviceLastReported: - dlr := e.(DeviceLastReported) - updateDeviceLastReportedConnected(dlr.DeviceName, lc, mdc, configuration) - break - case DeviceServiceLastReported: - dslr := e.(DeviceServiceLastReported) - updateDeviceServiceLastReportedConnected(dslr.DeviceName, lc, mdc, msc, configuration) - break - } - } else { - return - } - } - } - }() -} diff --git a/internal/core/data/errors/types.go b/internal/core/data/errors/types.go deleted file mode 100644 index c7aeaf35ac..0000000000 --- a/internal/core/data/errors/types.go +++ /dev/null @@ -1,172 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Inc. - * - * 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 errors - -import ( - "fmt" - "strings" - - "github.com/edgexfoundry/edgex-go/internal/pkg/db" -) - -type ErrCBORNotSupported struct { -} - -func (e ErrCBORNotSupported) Error() string { - return "CBOR payload is not yet supported" -} - -type ErrEventNotFound struct { - id string -} - -func (e ErrEventNotFound) Error() string { - return fmt.Sprintf("no event found for id %s", e.id) -} - -func NewErrEventNotFound(id string) error { - return ErrEventNotFound{id: id} -} - -type ErrValueDescriptorInvalid struct { - name string - err error -} - -func (b ErrValueDescriptorInvalid) Error() string { - return fmt.Sprintf("invalid value descriptor '%s': %v", b.name, b.err) -} - -func NewErrValueDescriptorInvalid(name string, err error) error { - return ErrValueDescriptorInvalid{name: name, err: err} -} - -type ErrValueDescriptorNotFound struct { - id string -} - -func (e ErrValueDescriptorNotFound) Error() string { - return fmt.Sprintf("no value descriptor for reading '%s'", e.id) -} - -func NewErrValueDescriptorNotFound(id string) error { - return ErrValueDescriptorNotFound{id: id} -} - -type ErrUnsupportedDatabase struct { - dbType string -} - -func (e ErrUnsupportedDatabase) Error() string { - return fmt.Sprintf("database type '%s' not supported", e.dbType) -} - -func NewErrUnsupportedDatabase(dbType string) error { - return ErrUnsupportedDatabase{dbType: dbType} -} - -type ErrUnsupportedPublisher struct { - pubType string -} - -func (e ErrUnsupportedPublisher) Error() string { - return fmt.Sprintf("publisher type '%s' not supported", e.pubType) -} - -func NewErrUnsupportedPublisher(pubType string) error { - return ErrUnsupportedPublisher{pubType: pubType} -} - -type ErrValueDescriptorInUse struct { - name string -} - -func (e ErrValueDescriptorInUse) Error() string { - return fmt.Sprintf("value descriptor '%s' still referenced by readings", e.name) -} - -func NewErrValueDescriptorInUse(name string) error { - return ErrValueDescriptorInUse{name: name} -} - -type ErrValueDescriptorsInUse struct { - names []string -} - -func (e ErrValueDescriptorsInUse) Error() string { - return fmt.Sprintf("value descriptors are still referenced by readings: %s", strings.Join(e.names, ",")) -} - -func NewErrValueDescriptorsInUse(names []string) error { - return ErrValueDescriptorsInUse{names: names} -} - -type ErrDuplicateValueDescriptorName struct { - name string -} - -func (e ErrDuplicateValueDescriptorName) Error() string { - return fmt.Sprintf("duplicate name for value descriptor: %s", e.name) -} - -func NewErrDuplicateValueDescriptorName(name string) error { - return ErrDuplicateValueDescriptorName{name: name} -} - -type ErrLimitExceeded struct { - limit int -} - -func (e ErrLimitExceeded) Error() string { - return fmt.Sprintf("limit %d exceeds configured max", e.limit) -} - -func NewErrLimitExceeded(limit int) error { - return ErrLimitExceeded{limit: limit} -} - -type ErrJsonDecoding struct { - name string -} - -func (e ErrJsonDecoding) Error() string { - return fmt.Sprintf("error decoding the reading: %s", e.name) -} - -func NewErrJsonDecoding(name string) error { - return ErrJsonDecoding{name: name} -} - -type ErrDbNotFound struct { -} - -func (e ErrDbNotFound) Error() string { - return db.ErrNotFound.Error() -} - -func NewErrDbNotFound() error { - return ErrDbNotFound{} -} - -type ErrInvalidId struct { - id string -} - -func (e ErrInvalidId) Error() string { - return fmt.Sprintf("invalid ID: %s", e.id) -} - -func NewErrInvalidId(id string) error { - return ErrInvalidId{id: id} -} diff --git a/internal/core/data/event.go b/internal/core/data/event.go deleted file mode 100644 index bb39cb27bd..0000000000 --- a/internal/core/data/event.go +++ /dev/null @@ -1,368 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * Copyright (c) 2019 Intel Corporation - * - * 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 data - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/edgexfoundry/edgex-go/internal/core/data/config" - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - "github.com/edgexfoundry/edgex-go/internal/core/data/interfaces" - "github.com/edgexfoundry/edgex-go/internal/pkg/correlation" - "github.com/edgexfoundry/edgex-go/internal/pkg/correlation/models" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/metadata" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/edgexfoundry/go-mod-messaging/v2/messaging" - msgTypes "github.com/edgexfoundry/go-mod-messaging/v2/pkg/types" -) - -const ( - ChecksumAlgoMD5 = "md5" - ChecksumAlgoxxHash = "xxHash" -) - -func countEvents(dbClient interfaces.DBClient) (int, error) { - count, err := dbClient.EventCount() - if err != nil { - return -1, err - } - return count, nil -} - -func countEventsByDevice( - device string, - ctx context.Context, - dbClient interfaces.DBClient, - mdc metadata.DeviceClient, - configuration *config.ConfigurationStruct) (int, error) { - - err := checkDevice(device, ctx, mdc, configuration) - if err != nil { - return -1, err - } - - count, err := dbClient.EventCountByDeviceId(device) - if err != nil { - return -1, fmt.Errorf("error obtaining count for device %s: %v", device, err) - } - return count, err -} - -func deleteEventsByAge(age int64, lc logger.LoggingClient, dbClient interfaces.DBClient) (int, error) { - events, err := dbClient.EventsOlderThanAge(age) - if err != nil { - return -1, err - } - - // Delete all the events - count := len(events) - for _, event := range events { - if err = deleteEvent(event, lc, dbClient); err != nil { - return -1, err - } - } - return count, nil -} - -func getEvents(limit int, dbClient interfaces.DBClient) ([]contract.Event, error) { - var err error - var events []contract.Event - - if limit <= 0 { - events, err = dbClient.Events() - } else { - events, err = dbClient.EventsWithLimit(limit) - } - - if err != nil { - return nil, err - } - return events, err -} - -func addNewEvent( - e models.Event, ctx context.Context, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - chEvents chan<- interface{}, - msgClient messaging.MessageClient, - mdc metadata.DeviceClient, - configuration *config.ConfigurationStruct) (string, error) { - - err := checkDevice(e.Device, ctx, mdc, configuration) - if err != nil { - return "", err - } - - if configuration.Writable.ValidateCheck { - lc.Debug("Validation enabled, parsing events") - for reading := range e.Readings { - // Check value descriptor - name := e.Readings[reading].Name - vd, err := dbClient.ValueDescriptorByName(name) - if err != nil { - if err == db.ErrNotFound { - return "", errors.NewErrValueDescriptorNotFound(name) - } else { - return "", err - } - } - err = isValidValueDescriptor(vd, e.Readings[reading]) - if err != nil { - return "", err - } - } - } - - // Add the event and readings to the database - if configuration.Writable.PersistData { - if e.Created == 0 { - e.Created = db.MakeTimestamp() - } - id, err := dbClient.AddEvent(e) - if err != nil { - return "", err - } - e.ID = id - } - - putEventOnQueue(e, ctx, lc, msgClient, configuration) // Push event to message bus for App Services to consume - chEvents <- DeviceLastReported{e.Device} // update last reported connected (device) - chEvents <- DeviceServiceLastReported{e.Device} // update last reported connected (device service) - - return e.ID, nil -} - -func updateEvent( - from models.Event, - ctx context.Context, - dbClient interfaces.DBClient, - mdc metadata.DeviceClient, - configuration *config.ConfigurationStruct) error { - - to, err := dbClient.EventById(from.ID) - if err != nil { - return errors.NewErrEventNotFound(from.ID) - } - - // Update the fields - if len(from.Device) > 0 { - // Check device - err = checkDevice(from.Device, ctx, mdc, configuration) - if err != nil { - return err - } - - // Set the device name on the event - to.Device = from.Device - } - if from.Pushed != 0 { - to.Pushed = from.Pushed - } - if from.Origin != 0 { - to.Origin = from.Origin - } - - mapped := models.Event{Event: to} - return dbClient.UpdateEvent(mapped) -} - -func deleteEventById(id string, lc logger.LoggingClient, dbClient interfaces.DBClient) error { - e, err := getEventById(id, dbClient) - if err != nil { - return err - } - - err = deleteEvent(e, lc, dbClient) - if err != nil { - return err - } - return nil -} - -// Delete the event and readings -func deleteEvent(e contract.Event, lc logger.LoggingClient, dbClient interfaces.DBClient) error { - for _, reading := range e.Readings { - if err := deleteReadingById(reading.Id, lc, dbClient); err != nil { - return err - } - } - - if err := dbClient.DeleteEventById(e.ID); err != nil { - return err - } - - return nil -} - -func deleteAllEvents(dbClient interfaces.DBClient) error { - return dbClient.ScrubAllEvents() -} - -func getEventById(id string, dbClient interfaces.DBClient) (contract.Event, error) { - e, err := dbClient.EventById(id) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrEventNotFound(id) - } - return contract.Event{}, err - } - return e, nil -} - -// updateEventPushDateByChecksum updates the pushed dated for all events with a matching checksum which -// have not already been marked pushed -func updateEventPushDateByChecksum( - checksum string, - ctx context.Context, - dbClient interfaces.DBClient, - mdc metadata.DeviceClient, - configuration *config.ConfigurationStruct) error { - - evts, err := dbClient.EventsByChecksum(checksum) - if err != nil { - return err - } - - for _, e := range evts { - e.Pushed = db.MakeTimestamp() - // Updating the event has the desired side-effect of removing the checksum. - // We only want the checksum for "marked pushed" functionality and once the event - // has been marked pushed there is no reason to keep the checksum around. - // The expectation is that above query will only return one result, but this is not guaranteed - err = updateEvent(models.Event{Event: e}, ctx, dbClient, mdc, configuration) - if err != nil { - return err - } - } - return nil -} - -func updateEventPushDate( - id string, - ctx context.Context, - dbClient interfaces.DBClient, - mdc metadata.DeviceClient, - configuration *config.ConfigurationStruct) error { - - e, err := getEventById(id, dbClient) - if err != nil { - return err - } - - e.Pushed = db.MakeTimestamp() - err = updateEvent(models.Event{Event: e}, ctx, dbClient, mdc, configuration) - if err != nil { - return err - } - return nil -} - -// Put event on the message queue to be processed by the rules engine -func putEventOnQueue( - evt models.Event, - ctx context.Context, - lc logger.LoggingClient, - msgClient messaging.MessageClient, - configuration *config.ConfigurationStruct) { - - lc.Debug("Putting event on message queue") - - evt.CorrelationId = correlation.FromContext(ctx) - // Re-marshal JSON content into bytes. - if clients.FromContext(ctx, clients.ContentType) == clients.ContentTypeJSON { - data, err := json.Marshal(evt) - if err != nil { - lc.Error(fmt.Sprintf("error marshaling event: %s", evt.String())) - return - } - evt.Bytes = data - } - - msgEnvelope := msgTypes.NewMessageEnvelope(evt.Bytes, ctx) - err := msgClient.Publish(msgEnvelope, configuration.MessageQueue.PublishTopicPrefix) - if err != nil { - lc.Error(fmt.Sprintf("Unable to send message for event: %s %v", evt.String(), err)) - } else { - lc.Debug(fmt.Sprintf( - "Event Published on message queue. Topic: %s, Correlation-id: %s ", - configuration.MessageQueue.PublishTopicPrefix, - msgEnvelope.CorrelationID, - )) - } -} - -func getEventsByDeviceIdLimit( - limit int, - deviceId string, - lc logger.LoggingClient, - dbClient interfaces.DBClient) ([]contract.Event, error) { - - eventList, err := dbClient.EventsForDeviceLimit(deviceId, limit) - if err != nil { - lc.Error(err.Error()) - return nil, err - } - - return eventList, nil -} - -func getEventsByCreationTime( - limit int, - start int64, - end int64, - lc logger.LoggingClient, - dbClient interfaces.DBClient) ([]contract.Event, error) { - - eventList, err := dbClient.EventsByCreationTime(start, end, limit) - if err != nil { - lc.Error(err.Error()) - return nil, err - } - - return eventList, nil -} - -func deleteEvents(deviceId string, dbClient interfaces.DBClient) (int, error) { - return dbClient.DeleteEventsByDevice(deviceId) -} - -func scrubPushedEvents(lc logger.LoggingClient, dbClient interfaces.DBClient) (int, error) { - lc.Info("Scrubbing events. Deleting all events that have been pushed") - - // Get the events - events, err := dbClient.EventsPushed() - if err != nil { - lc.Error(err.Error()) - return 0, err - } - - // Delete all the events - count := len(events) - for _, event := range events { - if err = deleteEvent(event, lc, dbClient); err != nil { - lc.Error(err.Error()) - return 0, err - } - } - - return count, nil -} diff --git a/internal/core/data/event_test.go b/internal/core/data/event_test.go deleted file mode 100644 index 7aa52f128b..0000000000 --- a/internal/core/data/event_test.go +++ /dev/null @@ -1,650 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Inc. - * - * 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 data - -import ( - "context" - "fmt" - "strconv" - "sync" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/core/data/config" - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - dbMock "github.com/edgexfoundry/edgex-go/internal/core/data/interfaces/mocks" - dataMocks "github.com/edgexfoundry/edgex-go/internal/core/data/mocks" - correlation "github.com/edgexfoundry/edgex-go/internal/pkg/correlation/models" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/edgexfoundry/go-mod-messaging/v2/messaging" - msgTypes "github.com/edgexfoundry/go-mod-messaging/v2/pkg/types" - - "github.com/google/uuid" - "github.com/stretchr/testify/mock" -) - -// Test methods -func TestEventCount(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("EventCount").Return(1, nil) - - c, err := countEvents(dbClientMock) - if err != nil { - t.Errorf(err.Error()) - } - - if c != 1 { - t.Errorf("expected event count 1, received: %d", c) - } -} - -func TestCountByDevice(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("EventCountByDeviceId", mock.Anything).Return(2, nil) - - count, err := countEventsByDevice( - testEvent.Device, - context.Background(), - dbClientMock, - dataMocks.NewMockDeviceClient(), - &config.ConfigurationStruct{}) - - if err != nil { - t.Errorf(err.Error()) - } - - if count == 0 { - t.Errorf("no events found") - } -} - -func buildEvents() []contract.Event { - return []contract.Event{ - { - ID: "1", - Device: testDeviceName, - Readings: []contract.Reading{ - {Id: "1"}, - {Id: "2"}, - }, - }, - } -} - -func newDeleteEventsOlderThanAgeMockDB() *dbMock.DBClient { - myMock := &dbMock.DBClient{} - - myMock.On("EventsOlderThanAge", mock.MatchedBy(func(age int64) bool { - return age == -1 - })).Return(buildEvents(), nil).Maybe() - - myMock.On("DeleteReadingById", mock.MatchedBy(func(id string) bool { - return id == "1" - })).Return(nil) - - myMock.On("DeleteReadingById", mock.MatchedBy(func(id string) bool { - return id == "2" - })).Return(nil) - - myMock.On("DeleteEventById", mock.MatchedBy(func(id string) bool { - return id == "1" - })).Return(nil) - - return myMock -} - -func TestDeleteByAge(t *testing.T) { - reset() - dbClientMock := newDeleteEventsOlderThanAgeMockDB() - count, err := deleteEventsByAge(-1, logger.NewMockClient(), dbClientMock) - if err != nil { - t.Errorf(err.Error()) - } - - if count == 0 { - t.Errorf("deleteEventsByAge returned 0; expected non-zero") - } - - dbClientMock.AssertExpectations(t) -} - -func TestDeleteEventByAgeErrorThrownByEventsOlderThanAge(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("EventsOlderThanAge", mock.MatchedBy(func(age int64) bool { - return age == -1 - })).Return([]contract.Event{}, fmt.Errorf("some error")) - - _, err := deleteEventsByAge(-1, logger.NewMockClient(), dbClientMock) - - if err == nil { - t.Errorf("Should throw error") - } -} - -func TestGetEvents(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("Events").Return([]contract.Event{testEvent}, nil) - - events, err := getEvents(0, dbClientMock) - if err != nil { - t.Errorf(err.Error()) - } - - if len(events) == 0 { - t.Errorf("no events found") - } - - if len(events) != 1 { - t.Errorf("expected 1 event") - } - - for e := range events { - testEventWithoutReadings(events[e], t) - } -} - -func newGetEventsWithLimitMockDB(expectedLimit int) *dbMock.DBClient { - myMock := &dbMock.DBClient{} - - myMock.On("EventsWithLimit", mock.MatchedBy(func(limit int) bool { - return limit == expectedLimit - })).Return(func(limit int) []contract.Event { - events := make([]contract.Event, 0) - for i := 0; i < limit; i++ { - events = append(events, testEvent) - } - return events - }, nil) - - return myMock -} - -func TestGetEventsWithLimit(t *testing.T) { - reset() - - limit := 1 - dbClientMock := newGetEventsWithLimitMockDB(limit) - - events, err := getEvents(limit, dbClientMock) - if err != nil { - t.Errorf(err.Error()) - } - - if len(events) != limit { - t.Errorf("expected %d event", limit) - } - - dbClientMock.AssertExpectations(t) -} - -func newAddEventMockDB(persist bool) *dbMock.DBClient { - myMock := &dbMock.DBClient{} - - if persist { - myMock.On("AddEvent", mock.Anything).Return("3c5badcb-2008-47f2-ba78-eb2d992f8422", nil) - } - - return myMock -} - -func TestAddEventWithPersistence(t *testing.T) { - reset() - - // no need to mock this since it's all in process - msgClient, _ := messaging.NewMessageClient(msgTypes.MessageBusConfig{ - PublishHost: msgTypes.HostInfo{ - Host: "*", - Protocol: "tcp", - Port: 5563, - }, - Type: "zero", - }) - - dbClientMock := newAddEventMockDB(true) - chEvents := make(chan interface{}, 10) - evt := contract.Event{Device: testDeviceName, Origin: testOrigin, Readings: buildReadings()} - // wire up handlers to listen for device events - bitEvents := make([]bool, 2) - wg := sync.WaitGroup{} - wg.Add(1) - go handleDomainEvents(bitEvents, chEvents, &wg, t) - - _, err := addNewEvent( - correlation.Event{Event: evt}, - context.Background(), - logger.NewMockClient(), - dbClientMock, - chEvents, - msgClient, - dataMocks.NewMockDeviceClient(), - &config.ConfigurationStruct{ - Writable: config.WritableInfo{ - PersistData: true, - }, - }) - - if err != nil { - t.Errorf(err.Error()) - } - - wg.Wait() - for i, val := range bitEvents { - if !val { - t.Errorf("event not received in timely fashion, index %v, TestAddEventWithPersistence", i) - } - } - - dbClientMock.AssertExpectations(t) -} - -func TestAddEventNoPersistence(t *testing.T) { - reset() - msgClient, _ := messaging.NewMessageClient(msgTypes.MessageBusConfig{ - PublishHost: msgTypes.HostInfo{ - Host: "*", - Protocol: "tcp", - Port: 5563, - }, - Type: "zero", - }) - - dbClientMock := newAddEventMockDB(false) - evt := contract.Event{Device: testDeviceName, Origin: testOrigin, Readings: buildReadings()} - // wire up handlers to listen for device events - bitEvents := make([]bool, 2) - chEvents := make(chan interface{}) - wg := sync.WaitGroup{} - wg.Add(1) - go handleDomainEvents(bitEvents, chEvents, &wg, t) - - _, err := addNewEvent( - correlation.Event{Event: evt}, - context.Background(), - logger.NewMockClient(), - dbClientMock, - chEvents, - msgClient, - dataMocks.NewMockDeviceClient(), - &config.ConfigurationStruct{ - Writable: config.WritableInfo{ - PersistData: false, - }, - }) - - if err != nil { - t.Errorf(err.Error()) - } - - wg.Wait() - for i, val := range bitEvents { - if !val { - t.Errorf("event not received in timely fashion, index %v, TestAddEventNoPersistence", i) - } - } - - dbClientMock.AssertExpectations(t) -} - -func TestUpdateEventNotFound(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("EventById", mock.Anything).Return(contract.Event{}, fmt.Errorf("Event not found")) - - id, _ := uuid.NewUUID() - evt := contract.Event{ID: id.String(), Device: "Not Found", Origin: testOrigin} - err := updateEvent( - correlation.Event{Event: evt}, - context.Background(), - dbClientMock, - dataMocks.NewMockDeviceClient(), - &config.ConfigurationStruct{}) - - if err != nil { - if x, ok := err.(errors.ErrEventNotFound); !ok { - t.Errorf("unexpected error type: %s", x.Error()) - } - } else { - t.Errorf("expected ErrEventNotFound") - } -} - -func newUpdateEventMockDB(expectedDevice string) *dbMock.DBClient { - myMock := &dbMock.DBClient{} - - myMock.On("EventById", mock.MatchedBy(func(id string) bool { - return id == testEvent.ID - })).Return(testEvent, nil).Maybe() - - myMock.On("UpdateEvent", mock.MatchedBy(func(event correlation.Event) bool { - return event.ID == testEvent.ID && event.Device == expectedDevice - })).Return(nil) - - return myMock -} - -func TestUpdateEvent(t *testing.T) { - reset() - expectedDevice := "Some Value" - dbClientMock := newUpdateEventMockDB(expectedDevice) - - evt := contract.Event{ID: testEvent.ID, Device: expectedDevice, Origin: testOrigin} - err := updateEvent( - correlation.Event{Event: evt}, - context.Background(), - dbClientMock, - dataMocks.NewMockDeviceClient(), - &config.ConfigurationStruct{}) - - if err != nil { - t.Errorf(err.Error()) - } - - dbClientMock.AssertExpectations(t) -} - -func TestDeleteAllEvents(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("ScrubAllEvents").Return(nil) - - err := deleteAllEvents(dbClientMock) - if err != nil { - t.Errorf(err.Error()) - } - dbClientMock.AssertExpectations(t) -} - -func TestGetEventById(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("EventById", mock.MatchedBy(func(id string) bool { - return id == testEvent.ID - })).Return(testEvent, nil) - - _, err := getEventById(testEvent.ID, dbClientMock) - if err != nil { - t.Errorf(err.Error()) - } - - dbClientMock.AssertExpectations(t) -} - -func TestGetEventByIdNotFound(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("EventById", mock.Anything).Return(testEvent, db.ErrNotFound) - - _, err := getEventById("abcxyz", dbClientMock) - if err != nil { - if x, ok := err.(errors.ErrEventNotFound); !ok { - t.Errorf(x.Error()) - } - } - - dbClientMock.AssertExpectations(t) -} - -func TestUpdateEventPushDate(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("EventById", mock.MatchedBy(func(id string) bool { - return id == testEvent.ID - })).Return(testEvent, nil) - dbClientMock.On("UpdateEvent", mock.MatchedBy(func(event correlation.Event) bool { - return event.ID == testEvent.ID - })).Return(nil) - - err := updateEventPushDate( - testEvent.ID, - context.Background(), - dbClientMock, - dataMocks.NewMockDeviceClient(), - &config.ConfigurationStruct{}) - - if err != nil { - t.Errorf(err.Error()) - } - - dbClientMock.AssertExpectations(t) -} - -func newDeleteEventMockDB() *dbMock.DBClient { - myMock := &dbMock.DBClient{} - myMock.On("EventById", mock.MatchedBy(func(id string) bool { - return id == testEvent.ID - })).Return(testEvent, nil) - myMock.On("DeleteReadingById", mock.MatchedBy(func(id string) bool { - return id == testEvent.Readings[0].Id - })).Return(nil) - myMock.On("DeleteReadingById", mock.MatchedBy(func(id string) bool { - return id == testEvent.Readings[1].Id - })).Return(nil) - myMock.On("DeleteEventById", mock.MatchedBy(func(id string) bool { - return id == testEvent.ID - })).Return(nil) - return myMock -} - -func TestDeleteEventById(t *testing.T) { - reset() - dbClientMock := newDeleteEventMockDB() - - err := deleteEventById(testEvent.ID, logger.NewMockClient(), dbClientMock) - if err != nil { - t.Errorf(err.Error()) - } - - dbClientMock.AssertExpectations(t) -} - -func TestDeleteEvent(t *testing.T) { - reset() - dbClientMock := newDeleteEventMockDB() - - err := deleteEvent(testEvent, logger.NewMockClient(), dbClientMock) - - if err != nil { - t.Errorf(err.Error()) - } - - _, err = getEventById(testEvent.ID, dbClientMock) - if err != nil { - if x, ok := err.(errors.ErrEventNotFound); !ok { - t.Errorf(x.Error()) - } - } - - dbClientMock.AssertExpectations(t) -} - -func TestDeleteEventEventDoesNotExist(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("DeleteEventById", mock.MatchedBy(func(id string) bool { - return id == testEvent.ID - })).Return(db.ErrNotFound) - dbClientMock.On("DeleteReadingsByDevice", mock.MatchedBy(func(device string) bool { - return device == testDeviceName - })).Return(nil) - - testEvent.Readings = nil - err := deleteEvent(testEvent, logger.NewMockClient(), dbClientMock) - - if err == nil { - t.Errorf("Event does not exist and should throw error") - } -} - -func TestDeleteEventReadingDoesNotExist(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("DeleteEventById", mock.MatchedBy(func(id string) bool { - return id == testEvent.ID - })).Return(db.ErrNotFound) - dbClientMock.On("DeleteReadingById", mock.MatchedBy(func(id string) bool { - return id == testEvent.Readings[0].Id || id == testEvent.Readings[1].Id - })).Return(db.ErrNotFound) - dbClientMock.On("DeleteReadingsByDevice", mock.MatchedBy(func(device string) bool { - return device == testDeviceName - })).Return(nil) - - err := deleteEvent(testEvent, logger.NewMockClient(), dbClientMock) - - if err == nil { - t.Errorf("Reading does not exist and should throw error") - } -} - -func TestGetEventsByDeviceIdLimit(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - - dbClientMock.On("EventsForDeviceLimit", mock.MatchedBy(func(deviceId string) bool { - return deviceId == "valid" - }), mock.Anything).Return([]contract.Event{testEvent}, nil) - - expectedList, expectedNil := getEventsByDeviceIdLimit(0, "valid", logger.NewMockClient(), dbClientMock) - - if expectedNil != nil { - t.Errorf("Should not throw error") - } - - if expectedList == nil { - t.Errorf("Should return a list of events") - } - - if expectedList[0].ID != testEvent.ID { - t.Errorf("Didn't successfully return testEvent") - } -} - -func TestGetEventsByDeviceIdLimitDBThrowsError(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - - dbClientMock.On("EventsForDeviceLimit", mock.MatchedBy(func(deviceId string) bool { - return deviceId == "error" - }), mock.Anything).Return(nil, fmt.Errorf("some error")) - - expectedNil, expectedErr := getEventsByDeviceIdLimit(0, "error", logger.NewMockClient(), dbClientMock) - - if expectedNil != nil { - t.Errorf("Should not return list") - } - - if expectedErr == nil { - t.Errorf("Should throw error") - } -} - -func TestGetEventsByCreationTime(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("EventsByCreationTime", mock.MatchedBy(func(start int64) bool { - return start == 0xF00D - }), mock.Anything, mock.Anything).Return([]contract.Event{}, nil) - - expectedReadings, expectedNil := getEventsByCreationTime(0, 0xF00D, 0, logger.NewMockClient(), dbClientMock) - - if expectedReadings == nil { - t.Errorf("Should return Events") - } - - if expectedNil != nil { - t.Errorf("Should not throw error") - } -} - -func TestGetEventsByCreationTimeDBThrowsError(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("EventsByCreationTime", mock.MatchedBy(func(start int64) bool { - return start == 0xBADF00D - }), mock.Anything, mock.Anything).Return(nil, fmt.Errorf("some error")) - - expectedNil, expectedErr := getEventsByCreationTime(0, 0xBADF00D, 0, logger.NewMockClient(), dbClientMock) - - if expectedNil != nil { - t.Errorf("Should not return list") - } - - if expectedErr == nil { - t.Errorf("Should throw error") - } -} - -func TestDeleteEvents(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("DeleteEventsByDevice", mock.MatchedBy(func(deviceId string) bool { - return deviceId == testUUIDString - })).Return(0, nil) - - _, expectedNil := deleteEvents(testUUIDString, dbClientMock) - - if expectedNil != nil { - t.Errorf("Should not throw error") - } - - dbClientMock.AssertExpectations(t) -} - -func TestScrubPushedEvents(t *testing.T) { - reset() - - pushedEvents := []contract.Event{testEvent, testEvent} - pushedEvents[1].ID = testUUIDString - - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("EventsPushed").Return(pushedEvents, nil) - dbClientMock.On("DeleteReadingById", mock.MatchedBy(func(id string) bool { - return id == pushedEvents[0].Readings[0].Id || id == pushedEvents[0].Readings[1].Id - })).Return(nil).Times(4) - - dbClientMock.On("DeleteEventById", mock.MatchedBy(func(id string) bool { - return id == pushedEvents[0].ID || id == pushedEvents[1].ID - })).Return(nil).Twice() - - expectedCount := 2 - actualCount, expectedNil := scrubPushedEvents(logger.NewMockClient(), dbClientMock) - - if actualCount != expectedCount { - t.Errorf("Expected %d deletions, was %d", expectedCount, actualCount) - } - - if expectedNil != nil { - t.Errorf("Should not throw error") - } -} - -func testEventWithoutReadings(event contract.Event, t *testing.T) { - if event.ID != testEvent.ID { - t.Error("eventId mismatch. expected " + testEvent.ID + " received " + event.ID) - } - - if event.Device != testEvent.Device { - t.Error("device mismatch. expected " + testDeviceName + " received " + event.Device) - } - - if event.Origin != testEvent.Origin { - t.Error("origin mismatch. expected " + strconv.FormatInt(testEvent.Origin, 10) + " received " + strconv.FormatInt(event.Origin, 10)) - } -} diff --git a/internal/core/data/init.go b/internal/core/data/init.go index 28d7fc5756..72bc1b6c2c 100644 --- a/internal/core/data/init.go +++ b/internal/core/data/init.go @@ -22,15 +22,10 @@ import ( dataContainer "github.com/edgexfoundry/edgex-go/internal/core/data/container" "github.com/edgexfoundry/edgex-go/internal/core/data/v2" "github.com/edgexfoundry/edgex-go/internal/core/data/v2/application" - errorContainer "github.com/edgexfoundry/edgex-go/internal/pkg/container" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/container" "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/startup" "github.com/edgexfoundry/go-mod-bootstrap/v2/di" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/metadata" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/urlclient/local" "github.com/gorilla/mux" ) @@ -49,31 +44,11 @@ func NewBootstrap(router *mux.Router) *Bootstrap { // BootstrapHandler fulfills the BootstrapHandler contract and performs initialization needed by the data service. func (b *Bootstrap) BootstrapHandler(ctx context.Context, wg *sync.WaitGroup, startupTimer startup.Timer, dic *di.Container) bool { - loadRestRoutes(b.router, dic) v2.LoadRestRoutes(b.router, dic) configuration := dataContainer.ConfigurationFrom(dic.Get) lc := container.LoggingClientFrom(dic.Get) - mdc := metadata.NewDeviceClient(local.New(configuration.Clients[clients.CoreMetaDataServiceKey].Url() + clients.ApiDeviceRoute)) - msc := metadata.NewDeviceServiceClient(local.New(configuration.Clients[clients.CoreMetaDataServiceKey].Url() + clients.ApiDeviceRoute)) - - chEvents := make(chan interface{}, 100) - // initialize event handlers - initEventHandlers(lc, chEvents, mdc, msc, configuration) - - dic.Update(di.ServiceConstructorMap{ - dataContainer.MetadataDeviceClientName: func(get di.Get) interface{} { - return mdc - }, - dataContainer.EventsChannelName: func(get di.Get) interface{} { - return chEvents - }, - errorContainer.ErrorHandlerName: func(get di.Get) interface{} { - return errorconcept.NewErrorHandler(lc) - }, - }) - if configuration.MessageQueue.SubscribeEnabled { err := application.SubscribeEvents(ctx, dic) if err != nil { diff --git a/internal/core/data/interfaces/db.go b/internal/core/data/interfaces/db.go deleted file mode 100644 index 86520a7db4..0000000000 --- a/internal/core/data/interfaces/db.go +++ /dev/null @@ -1,202 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * - * 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 interfaces - -import ( - "github.com/edgexfoundry/edgex-go/internal/pkg/correlation/models" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type DBClient interface { - CloseSession() - - // ********************** EVENT FUNCTIONS ******************************* - // NOTE: Readings that contain binary data will not be persisted. - - // Return all the events - // UnexpectedError - failed to retrieve events from the database - Events() ([]contract.Event, error) - - // Return events up to the number specified - // UnexpectedError - failed to retrieve events from the database - EventsWithLimit(limit int) ([]contract.Event, error) - - // Add a new event - // UnexpectedError - failed to add to database - // NoValueDescriptor - no existing value descriptor for a reading in the event - AddEvent(e models.Event) (string, error) - - // Update an event - do NOT update readings - // UnexpectedError - problem updating in database - // NotFound - no event with the ID was found - UpdateEvent(e models.Event) error - - // Get an event by id - EventById(id string) (contract.Event, error) - - // Get all events with a matching checksum - EventsByChecksum(checksum string) ([]contract.Event, error) - - // Get the number of events in Core Data - EventCount() (int, error) - - // Get the number of events in Core Data for the device specified by id - EventCountByDeviceId(id string) (int, error) - - // Update an event by ID - // Set the pushed variable to the current time - // 404 - Event not found - // 503 - Unexpected problems - // UpdateEventById(id string) error - - // Delete an event by ID and all of its readings - // 404 - Event not found - // 503 - Unexpected problems - DeleteEventById(id string) error - - // Delete events associated with the specified device - DeleteEventsByDevice(deviceId string) (int, error) - - // Get a list of events based on the device id and limit - EventsForDeviceLimit(id string, limit int) ([]contract.Event, error) - - // Get a list of events based on the device id - EventsForDevice(id string) ([]contract.Event, error) - - // Delete all of the events by the device id (and the readings) - // DeleteEventsByDeviceId(id string) error - - // Return a list of events whos creation time is between startTime and endTime - // Limit the number of results by limit - EventsByCreationTime(startTime, endTime int64, limit int) ([]contract.Event, error) - - // Return a list of readings for a device filtered by the value descriptor and limited by the limit - // The readings are linked to the device through an event - ReadingsByDeviceAndValueDescriptor(deviceId, valueDescriptor string, limit int) ([]contract.Reading, error) - - // Remove all the events that are older than the given age - // Return the number of events removed - // RemoveEventByAge(age int64) (int, error) - - // Get events that are older than a age - EventsOlderThanAge(age int64) ([]contract.Event, error) - - // Remove all the events that have been pushed - // func (dbc *DBClient) ScrubEvents()(int, error) - - // Get events that have been pushed (pushed field is not 0) - EventsPushed() ([]contract.Event, error) - - // Delete all readings and events - ScrubAllEvents() error - - // ********************* READING FUNCTIONS ************************* - // NOTE: Readings that contain binary data will not be persisted. - - // Return a list of readings sorted by reading id - Readings() ([]contract.Reading, error) - - // Post a new reading - // Check if valuedescriptor exists in the database - AddReading(r contract.Reading) (string, error) - - // Update a reading - // 404 - reading cannot be found - // 409 - Value descriptor doesn't exist - // 503 - unknown issues - UpdateReading(r contract.Reading) error - - // Get a reading by ID - ReadingById(id string) (contract.Reading, error) - - // Get the number of readings in core data - ReadingCount() (int, error) - - // Delete a reading by ID - // 404 - can't find the reading with the given id - DeleteReadingById(id string) error - - // DeleteReadingsByDevice delete all readings associated with the specified Device - DeleteReadingsByDevice(device string) error - - // Return a list of readings for the given device (id or name) - // 404 - meta data checking enabled and can't find the device - // Sort the list of readings on creation date - ReadingsByDevice(id string, limit int) ([]contract.Reading, error) - - // Return a list of readings for the given value descriptor - // 413 - the number exceeds the current max limit - ReadingsByValueDescriptor(name string, limit int) ([]contract.Reading, error) - - // Return a list of readings whose name is in the list of value descriptor names - ReadingsByValueDescriptorNames(names []string, limit int) ([]contract.Reading, error) - - // Return a list of readings specified by the UOM label - // ReadingsByUomLabel(uomLabel string, limit int)([]contract.Reading, error) - - // Return a list of readings based on the label (value descriptor) - // 413 - limit exceeded - // ReadingsByLabel(label string, limit int) ([]contract.Reading, error) - - // Return a list of readings who's value descriptor has the type - // ReadingsByType(typeString string, limit int) ([]contract.Reading, error) - - // Return a list of readings whos created time is between the start and end times - ReadingsByCreationTime(start, end int64, limit int) ([]contract.Reading, error) - - // ************************** VALUE DESCRIPTOR FUNCTIONS *************************** - // Add a value descriptor - // 409 - Formatting is bad or it is not unique - // 503 - Unexpected - // TODO: Check for valid printf formatting - AddValueDescriptor(v contract.ValueDescriptor) (string, error) - - // Return a list of all the value descriptors - // 513 Service Unavailable - database problems - ValueDescriptors() ([]contract.ValueDescriptor, error) - - // Update a value descriptor - // First use the ID for identification, then the name - // TODO: Check for the valid printf formatting - // 404 not found if the value descriptor cannot be found by the identifiers - UpdateValueDescriptor(v contract.ValueDescriptor) error - - // Delete a value descriptor based on the ID - DeleteValueDescriptorById(id string) error - - // Return a value descriptor based on the name - ValueDescriptorByName(name string) (contract.ValueDescriptor, error) - - // Return value descriptors based on the names - ValueDescriptorsByName(names []string) ([]contract.ValueDescriptor, error) - - // Delete a valuedescriptor based on the name - // DeleteValueDescriptorByName(name string) error - - // Return a value descriptor based on the id - ValueDescriptorById(id string) (contract.ValueDescriptor, error) - - // Return value descriptors based on the unit of measure label - ValueDescriptorsByUomLabel(uomLabel string) ([]contract.ValueDescriptor, error) - - // Return value descriptors based on the label - ValueDescriptorsByLabel(label string) ([]contract.ValueDescriptor, error) - - // Return a list of value descriptors based on their type - ValueDescriptorsByType(t string) ([]contract.ValueDescriptor, error) - - // Delete all value descriptors - ScrubAllValueDescriptors() error -} diff --git a/internal/core/data/interfaces/mocks/DBClient.go b/internal/core/data/interfaces/mocks/DBClient.go deleted file mode 100644 index 9bde75d989..0000000000 --- a/internal/core/data/interfaces/mocks/DBClient.go +++ /dev/null @@ -1,812 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import go_mod_core_contractsmodels "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/edgex-go/internal/pkg/correlation/models" - -// DBClient is an autogenerated mock type for the DBClient type -type DBClient struct { - mock.Mock -} - -// AddEvent provides a mock function with given fields: e -func (_m *DBClient) AddEvent(e models.Event) (string, error) { - ret := _m.Called(e) - - var r0 string - if rf, ok := ret.Get(0).(func(models.Event) string); ok { - r0 = rf(e) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.Event) error); ok { - r1 = rf(e) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AddReading provides a mock function with given fields: r -func (_m *DBClient) AddReading(r go_mod_core_contractsmodels.Reading) (string, error) { - ret := _m.Called(r) - - var r0 string - if rf, ok := ret.Get(0).(func(go_mod_core_contractsmodels.Reading) string); ok { - r0 = rf(r) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(go_mod_core_contractsmodels.Reading) error); ok { - r1 = rf(r) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AddValueDescriptor provides a mock function with given fields: v -func (_m *DBClient) AddValueDescriptor(v go_mod_core_contractsmodels.ValueDescriptor) (string, error) { - ret := _m.Called(v) - - var r0 string - if rf, ok := ret.Get(0).(func(go_mod_core_contractsmodels.ValueDescriptor) string); ok { - r0 = rf(v) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(go_mod_core_contractsmodels.ValueDescriptor) error); ok { - r1 = rf(v) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// CloseSession provides a mock function with given fields: -func (_m *DBClient) CloseSession() { - _m.Called() -} - -// DeleteEventById provides a mock function with given fields: id -func (_m *DBClient) DeleteEventById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteEventsByDevice provides a mock function with given fields: deviceId -func (_m *DBClient) DeleteEventsByDevice(deviceId string) (int, error) { - ret := _m.Called(deviceId) - - var r0 int - if rf, ok := ret.Get(0).(func(string) int); ok { - r0 = rf(deviceId) - } else { - r0 = ret.Get(0).(int) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(deviceId) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DeleteReadingById provides a mock function with given fields: id -func (_m *DBClient) DeleteReadingById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteReadingsByDevice provides a mock function with given fields: device -func (_m *DBClient) DeleteReadingsByDevice(device string) error { - ret := _m.Called(device) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(device) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteValueDescriptorById provides a mock function with given fields: id -func (_m *DBClient) DeleteValueDescriptorById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// EventById provides a mock function with given fields: id -func (_m *DBClient) EventById(id string) (go_mod_core_contractsmodels.Event, error) { - ret := _m.Called(id) - - var r0 go_mod_core_contractsmodels.Event - if rf, ok := ret.Get(0).(func(string) go_mod_core_contractsmodels.Event); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(go_mod_core_contractsmodels.Event) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// EventCount provides a mock function with given fields: -func (_m *DBClient) EventCount() (int, error) { - ret := _m.Called() - - var r0 int - if rf, ok := ret.Get(0).(func() int); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int) - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// EventCountByDeviceId provides a mock function with given fields: id -func (_m *DBClient) EventCountByDeviceId(id string) (int, error) { - ret := _m.Called(id) - - var r0 int - if rf, ok := ret.Get(0).(func(string) int); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(int) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Events provides a mock function with given fields: -func (_m *DBClient) Events() ([]go_mod_core_contractsmodels.Event, error) { - ret := _m.Called() - - var r0 []go_mod_core_contractsmodels.Event - if rf, ok := ret.Get(0).(func() []go_mod_core_contractsmodels.Event); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.Event) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// EventsByChecksum provides a mock function with given fields: checksum -func (_m *DBClient) EventsByChecksum(checksum string) ([]go_mod_core_contractsmodels.Event, error) { - ret := _m.Called(checksum) - - var r0 []go_mod_core_contractsmodels.Event - if rf, ok := ret.Get(0).(func(string) []go_mod_core_contractsmodels.Event); ok { - r0 = rf(checksum) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.Event) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(checksum) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// EventsByCreationTime provides a mock function with given fields: startTime, endTime, limit -func (_m *DBClient) EventsByCreationTime(startTime int64, endTime int64, limit int) ([]go_mod_core_contractsmodels.Event, error) { - ret := _m.Called(startTime, endTime, limit) - - var r0 []go_mod_core_contractsmodels.Event - if rf, ok := ret.Get(0).(func(int64, int64, int) []go_mod_core_contractsmodels.Event); ok { - r0 = rf(startTime, endTime, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.Event) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int64, int64, int) error); ok { - r1 = rf(startTime, endTime, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// EventsForDevice provides a mock function with given fields: id -func (_m *DBClient) EventsForDevice(id string) ([]go_mod_core_contractsmodels.Event, error) { - ret := _m.Called(id) - - var r0 []go_mod_core_contractsmodels.Event - if rf, ok := ret.Get(0).(func(string) []go_mod_core_contractsmodels.Event); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.Event) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// EventsForDeviceLimit provides a mock function with given fields: id, limit -func (_m *DBClient) EventsForDeviceLimit(id string, limit int) ([]go_mod_core_contractsmodels.Event, error) { - ret := _m.Called(id, limit) - - var r0 []go_mod_core_contractsmodels.Event - if rf, ok := ret.Get(0).(func(string, int) []go_mod_core_contractsmodels.Event); ok { - r0 = rf(id, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.Event) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, int) error); ok { - r1 = rf(id, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// EventsOlderThanAge provides a mock function with given fields: age -func (_m *DBClient) EventsOlderThanAge(age int64) ([]go_mod_core_contractsmodels.Event, error) { - ret := _m.Called(age) - - var r0 []go_mod_core_contractsmodels.Event - if rf, ok := ret.Get(0).(func(int64) []go_mod_core_contractsmodels.Event); ok { - r0 = rf(age) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.Event) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int64) error); ok { - r1 = rf(age) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// EventsPushed provides a mock function with given fields: -func (_m *DBClient) EventsPushed() ([]go_mod_core_contractsmodels.Event, error) { - ret := _m.Called() - - var r0 []go_mod_core_contractsmodels.Event - if rf, ok := ret.Get(0).(func() []go_mod_core_contractsmodels.Event); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.Event) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// EventsWithLimit provides a mock function with given fields: limit -func (_m *DBClient) EventsWithLimit(limit int) ([]go_mod_core_contractsmodels.Event, error) { - ret := _m.Called(limit) - - var r0 []go_mod_core_contractsmodels.Event - if rf, ok := ret.Get(0).(func(int) []go_mod_core_contractsmodels.Event); ok { - r0 = rf(limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.Event) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ReadingById provides a mock function with given fields: id -func (_m *DBClient) ReadingById(id string) (go_mod_core_contractsmodels.Reading, error) { - ret := _m.Called(id) - - var r0 go_mod_core_contractsmodels.Reading - if rf, ok := ret.Get(0).(func(string) go_mod_core_contractsmodels.Reading); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(go_mod_core_contractsmodels.Reading) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ReadingCount provides a mock function with given fields: -func (_m *DBClient) ReadingCount() (int, error) { - ret := _m.Called() - - var r0 int - if rf, ok := ret.Get(0).(func() int); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int) - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Readings provides a mock function with given fields: -func (_m *DBClient) Readings() ([]go_mod_core_contractsmodels.Reading, error) { - ret := _m.Called() - - var r0 []go_mod_core_contractsmodels.Reading - if rf, ok := ret.Get(0).(func() []go_mod_core_contractsmodels.Reading); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.Reading) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ReadingsByCreationTime provides a mock function with given fields: start, end, limit -func (_m *DBClient) ReadingsByCreationTime(start int64, end int64, limit int) ([]go_mod_core_contractsmodels.Reading, error) { - ret := _m.Called(start, end, limit) - - var r0 []go_mod_core_contractsmodels.Reading - if rf, ok := ret.Get(0).(func(int64, int64, int) []go_mod_core_contractsmodels.Reading); ok { - r0 = rf(start, end, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.Reading) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int64, int64, int) error); ok { - r1 = rf(start, end, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ReadingsByDevice provides a mock function with given fields: id, limit -func (_m *DBClient) ReadingsByDevice(id string, limit int) ([]go_mod_core_contractsmodels.Reading, error) { - ret := _m.Called(id, limit) - - var r0 []go_mod_core_contractsmodels.Reading - if rf, ok := ret.Get(0).(func(string, int) []go_mod_core_contractsmodels.Reading); ok { - r0 = rf(id, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.Reading) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, int) error); ok { - r1 = rf(id, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ReadingsByDeviceAndValueDescriptor provides a mock function with given fields: deviceId, valueDescriptor, limit -func (_m *DBClient) ReadingsByDeviceAndValueDescriptor(deviceId string, valueDescriptor string, limit int) ([]go_mod_core_contractsmodels.Reading, error) { - ret := _m.Called(deviceId, valueDescriptor, limit) - - var r0 []go_mod_core_contractsmodels.Reading - if rf, ok := ret.Get(0).(func(string, string, int) []go_mod_core_contractsmodels.Reading); ok { - r0 = rf(deviceId, valueDescriptor, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.Reading) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, string, int) error); ok { - r1 = rf(deviceId, valueDescriptor, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ReadingsByValueDescriptor provides a mock function with given fields: name, limit -func (_m *DBClient) ReadingsByValueDescriptor(name string, limit int) ([]go_mod_core_contractsmodels.Reading, error) { - ret := _m.Called(name, limit) - - var r0 []go_mod_core_contractsmodels.Reading - if rf, ok := ret.Get(0).(func(string, int) []go_mod_core_contractsmodels.Reading); ok { - r0 = rf(name, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.Reading) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, int) error); ok { - r1 = rf(name, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ReadingsByValueDescriptorNames provides a mock function with given fields: names, limit -func (_m *DBClient) ReadingsByValueDescriptorNames(names []string, limit int) ([]go_mod_core_contractsmodels.Reading, error) { - ret := _m.Called(names, limit) - - var r0 []go_mod_core_contractsmodels.Reading - if rf, ok := ret.Get(0).(func([]string, int) []go_mod_core_contractsmodels.Reading); ok { - r0 = rf(names, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.Reading) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func([]string, int) error); ok { - r1 = rf(names, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ScrubAllEvents provides a mock function with given fields: -func (_m *DBClient) ScrubAllEvents() error { - ret := _m.Called() - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ScrubAllValueDescriptors provides a mock function with given fields: -func (_m *DBClient) ScrubAllValueDescriptors() error { - ret := _m.Called() - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateEvent provides a mock function with given fields: e -func (_m *DBClient) UpdateEvent(e models.Event) error { - ret := _m.Called(e) - - var r0 error - if rf, ok := ret.Get(0).(func(models.Event) error); ok { - r0 = rf(e) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateReading provides a mock function with given fields: r -func (_m *DBClient) UpdateReading(r go_mod_core_contractsmodels.Reading) error { - ret := _m.Called(r) - - var r0 error - if rf, ok := ret.Get(0).(func(go_mod_core_contractsmodels.Reading) error); ok { - r0 = rf(r) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateValueDescriptor provides a mock function with given fields: v -func (_m *DBClient) UpdateValueDescriptor(v go_mod_core_contractsmodels.ValueDescriptor) error { - ret := _m.Called(v) - - var r0 error - if rf, ok := ret.Get(0).(func(go_mod_core_contractsmodels.ValueDescriptor) error); ok { - r0 = rf(v) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ValueDescriptorById provides a mock function with given fields: id -func (_m *DBClient) ValueDescriptorById(id string) (go_mod_core_contractsmodels.ValueDescriptor, error) { - ret := _m.Called(id) - - var r0 go_mod_core_contractsmodels.ValueDescriptor - if rf, ok := ret.Get(0).(func(string) go_mod_core_contractsmodels.ValueDescriptor); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(go_mod_core_contractsmodels.ValueDescriptor) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ValueDescriptorByName provides a mock function with given fields: name -func (_m *DBClient) ValueDescriptorByName(name string) (go_mod_core_contractsmodels.ValueDescriptor, error) { - ret := _m.Called(name) - - var r0 go_mod_core_contractsmodels.ValueDescriptor - if rf, ok := ret.Get(0).(func(string) go_mod_core_contractsmodels.ValueDescriptor); ok { - r0 = rf(name) - } else { - r0 = ret.Get(0).(go_mod_core_contractsmodels.ValueDescriptor) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ValueDescriptors provides a mock function with given fields: -func (_m *DBClient) ValueDescriptors() ([]go_mod_core_contractsmodels.ValueDescriptor, error) { - ret := _m.Called() - - var r0 []go_mod_core_contractsmodels.ValueDescriptor - if rf, ok := ret.Get(0).(func() []go_mod_core_contractsmodels.ValueDescriptor); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.ValueDescriptor) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ValueDescriptorsByLabel provides a mock function with given fields: label -func (_m *DBClient) ValueDescriptorsByLabel(label string) ([]go_mod_core_contractsmodels.ValueDescriptor, error) { - ret := _m.Called(label) - - var r0 []go_mod_core_contractsmodels.ValueDescriptor - if rf, ok := ret.Get(0).(func(string) []go_mod_core_contractsmodels.ValueDescriptor); ok { - r0 = rf(label) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.ValueDescriptor) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(label) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ValueDescriptorsByName provides a mock function with given fields: names -func (_m *DBClient) ValueDescriptorsByName(names []string) ([]go_mod_core_contractsmodels.ValueDescriptor, error) { - ret := _m.Called(names) - - var r0 []go_mod_core_contractsmodels.ValueDescriptor - if rf, ok := ret.Get(0).(func([]string) []go_mod_core_contractsmodels.ValueDescriptor); ok { - r0 = rf(names) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.ValueDescriptor) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func([]string) error); ok { - r1 = rf(names) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ValueDescriptorsByType provides a mock function with given fields: t -func (_m *DBClient) ValueDescriptorsByType(t string) ([]go_mod_core_contractsmodels.ValueDescriptor, error) { - ret := _m.Called(t) - - var r0 []go_mod_core_contractsmodels.ValueDescriptor - if rf, ok := ret.Get(0).(func(string) []go_mod_core_contractsmodels.ValueDescriptor); ok { - r0 = rf(t) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.ValueDescriptor) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(t) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ValueDescriptorsByUomLabel provides a mock function with given fields: uomLabel -func (_m *DBClient) ValueDescriptorsByUomLabel(uomLabel string) ([]go_mod_core_contractsmodels.ValueDescriptor, error) { - ret := _m.Called(uomLabel) - - var r0 []go_mod_core_contractsmodels.ValueDescriptor - if rf, ok := ret.Get(0).(func(string) []go_mod_core_contractsmodels.ValueDescriptor); ok { - r0 = rf(uomLabel) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]go_mod_core_contractsmodels.ValueDescriptor) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(uomLabel) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/data/io.go b/internal/core/data/io.go deleted file mode 100644 index c8589bb3dc..0000000000 --- a/internal/core/data/io.go +++ /dev/null @@ -1,110 +0,0 @@ -package data - -import ( - "context" - "crypto/md5" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net/http" - "strings" - - "github.com/edgexfoundry/edgex-go/internal/core/data/config" - "github.com/edgexfoundry/edgex-go/internal/pkg/correlation/models" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - - "github.com/OneOfOne/xxhash" - "github.com/fxamacker/cbor/v2" -) - -const ( - checksumContextKey = "payload-checksum" - maxEventSize = int64(25 * 1e6) // 25 MB -) - -// ErrUnsupportedContentType an error used when a request is received with an unsupported content type -type ErrUnsupportedContentType struct { - ContentType string -} - -func (e ErrUnsupportedContentType) Error() string { - return "Unsupported content type: '" + e.ContentType + "'" -} - -// EventReader unmarshals a request body into an Event type -type EventReader interface { - Read(reader io.Reader, ctx *context.Context) (models.Event, error) -} - -// jsonReader handles unmarshaling of a JSON request body payload -type jsonReader struct{} - -// Read reads and converts the request's JSON event data into an Event struct -func (jsonReader) Read(reader io.Reader, ctx *context.Context) (models.Event, error) { - c := context.WithValue(*ctx, clients.ContentType, clients.ContentTypeJSON) - *ctx = c - - event := models.Event{} - err := json.NewDecoder(reader).Decode(&event) - if err != nil { - return event, err - } - - return event, nil -} - -// NewJsonReader creates a new instance of cborReader. -func NewJsonReader() jsonReader { - return jsonReader{} -} - -// cborReader handles unmarshaling of a CBOR request body payload -type cborReader struct { - configuration *config.ConfigurationStruct -} - -// NewCborReader creates a new instance of cborReader. -func NewCborReader(configuration *config.ConfigurationStruct) cborReader { - return cborReader{configuration: configuration} -} - -// Read reads and converts the request's CBOR event data into an Event struct -func (cr cborReader) Read(reader io.Reader, ctx *context.Context) (models.Event, error) { - c := context.WithValue(*ctx, clients.ContentType, clients.ContentTypeCBOR) - event := models.Event{} - bytes, err := ioutil.ReadAll(io.LimitReader(reader, maxEventSize)) - if err != nil { - return event, err - } - - err = cbor.Unmarshal(bytes, &event) - if err != nil { - return event, err - } - - switch cr.configuration.Writable.ChecksumAlgo { - case ChecksumAlgoxxHash: - event.Checksum = fmt.Sprintf("%x", xxhash.Checksum64(bytes)) - default: - event.Checksum = fmt.Sprintf("%x", md5.Sum(bytes)) - } - c = context.WithValue(c, checksumContextKey, event.Checksum) - *ctx = c - event.Bytes = bytes - - return event, nil -} - -// NewRequestReader returns a BodyReader capable of processing the request body -func NewRequestReader(request *http.Request, configuration *config.ConfigurationStruct) EventReader { - contentType := request.Header.Get(clients.ContentType) - - switch strings.ToLower(contentType) { - case clients.ContentTypeCBOR: - return NewCborReader(configuration) - default: - return NewJsonReader() - } -} diff --git a/internal/core/data/io_test.go b/internal/core/data/io_test.go deleted file mode 100644 index 860410c420..0000000000 --- a/internal/core/data/io_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package data - -import ( - "bytes" - "context" - "io/ioutil" - "net/http" - "net/http/httptest" - "reflect" - "strings" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/core/data/config" - "github.com/edgexfoundry/edgex-go/internal/pkg/correlation/models" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -var TestEvent = contract.Event{ - ID: "TestEvent", - Pushed: 0623, - Device: "TestDevice", - Created: 1553247000, - Modified: 1573900200, - Origin: 9, - Readings: buildReadings(), -} - -func TestNewReader(t *testing.T) { - type args struct { - contentType string - } - tests := []struct { - name string - args args - want EventReader - wantErr bool - }{ - { - name: "Get Json Reader", - args: args{ - contentType: clients.ContentTypeJSON, - }, - want: jsonReader{}, - }, - { - name: "Get Cbor Reader", - args: args{ - contentType: clients.ContentTypeCBOR, - }, - want: cborReader{}, - }, - { - name: "Get Reader for unknown type", - args: args{ - contentType: "unkown-type", - }, - want: jsonReader{}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - request := newRequestWithContentType(tt.args.contentType) - got := NewRequestReader(request, &config.ConfigurationStruct{}) - - eet := reflect.TypeOf(tt.want) - aet := reflect.TypeOf(got) - if !aet.AssignableTo(eet) { - t.Errorf("Expected reader of type %v, but got an reader of type %v", eet, aet) - } - }) - } -} - -func TestJsonSerialization(t *testing.T) { - event := models.Event{Event: TestEvent} - - data, _ := event.MarshalJSON() - - r := ioutil.NopCloser(bytes.NewBuffer(data)) - - ctx := context.Background() - jsonReader := NewJsonReader() - _, err := jsonReader.Read(r, &ctx) - - if err != nil { - t.Errorf("Should not encounter an error") - } - -} - -func TestCborSerialization(t *testing.T) { - reset() - event := models.Event{Event: TestEvent} - data := event.CBOR() - r := ioutil.NopCloser(bytes.NewBuffer(data)) - - cborReader := NewCborReader(&config.ConfigurationStruct{}) - - ctx := context.Background() - result, err := cborReader.Read(r, &ctx) - - if err != nil { - t.Errorf("Should not encounter an error") - } - if !reflect.DeepEqual(*result.ToContract(), TestEvent) { - t.Errorf("TestCborSerialization() = %v, want %v", result, TestEvent) - } - -} - -func newRequestWithContentType(contentType string) *http.Request { - req := httptest.NewRequest(http.MethodGet, "/", strings.NewReader("Test body")) - req.Header.Set(clients.ContentType, contentType) - return req -} diff --git a/internal/core/data/main.go b/internal/core/data/main.go index 55e0541d74..d285b5cfd4 100644 --- a/internal/core/data/main.go +++ b/internal/core/data/main.go @@ -25,7 +25,6 @@ import ( dataContainer "github.com/edgexfoundry/edgex-go/internal/core/data/container" "github.com/edgexfoundry/edgex-go/internal/core/data/messaging" v2DataContainer "github.com/edgexfoundry/edgex-go/internal/core/data/v2/bootstrap/container" - "github.com/edgexfoundry/edgex-go/internal/pkg/bootstrap/handlers/database" "github.com/edgexfoundry/edgex-go/internal/pkg/telemetry" v2Handlers "github.com/edgexfoundry/edgex-go/internal/pkg/v2/bootstrap/handlers" @@ -40,7 +39,7 @@ import ( "github.com/gorilla/mux" ) -func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router, readyStream chan<- bool) { +func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router) { startupTimer := startup.NewStartUpTimer(clients.CoreDataServiceKey) // All common command-line flags have been moved to DefaultCommonFlags. Service specific flags can be add here, @@ -73,14 +72,12 @@ func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router, re dic, true, []interfaces.BootstrapHandler{ - database.NewDatabaseForCoreData(httpServer, configuration).BootstrapHandler, v2Handlers.NewDatabase(httpServer, configuration, v2DataContainer.DBClientInterfaceName).BootstrapHandler, // add v2 db client bootstrap handler messaging.BootstrapHandler, NewBootstrap(router).BootstrapHandler, telemetry.BootstrapHandler, httpServer.BootstrapHandler, handlers.NewStartMessage(clients.CoreDataServiceKey, edgex.Version).BootstrapHandler, - handlers.NewReady(httpServer, readyStream).BootstrapHandler, }, ) } diff --git a/internal/core/data/mocks/device.go b/internal/core/data/mocks/device.go deleted file mode 100644 index cc43ce1781..0000000000 --- a/internal/core/data/mocks/device.go +++ /dev/null @@ -1,83 +0,0 @@ -/******************************************************************************** - * Copyright 2019 Dell Inc. - * - * 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 mocks - -import ( - "context" - "fmt" - "net/http" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/types" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/google/uuid" - "github.com/stretchr/testify/mock" - - "github.com/edgexfoundry/edgex-go/internal/mocks" -) - -const testDeviceName string = "Test Device" - -// NewMockDeviceClient creates a new mock DeviceClient which has some general mocking behavior defined. -func NewMockDeviceClient() *mocks.DeviceClient { - client := &mocks.DeviceClient{} - - protocols := getProtocols() - - mockDeviceResultFn := func(ctx context.Context, id string) contract.Device { - if len(id) > 0 { - return contract.Device{Id: id, Name: testDeviceName, Protocols: protocols} - } - return contract.Device{} - } - client.On("Device", context.Background(), "valid").Return(mockDeviceResultFn, nil) - client.On("Device", context.Background(), "404").Return(mockDeviceResultFn, - types.NewErrServiceClient(http.StatusNotFound, []byte{})) - client.On("Device", context.Background(), mock.Anything).Return(mockDeviceResultFn, fmt.Errorf("some error")) - - mockDeviceForNameResultFn := func(ctx context.Context, name string) contract.Device { - device := contract.Device{Id: uuid.New().String(), Name: name, Protocols: protocols} - - return device - } - client.On("DeviceForName", context.Background(), testDeviceName).Return(mockDeviceForNameResultFn, nil) - client.On("DeviceForName", context.Background(), "404").Return(mockDeviceForNameResultFn, - types.NewErrServiceClient(http.StatusNotFound, []byte{})) - client.On("DeviceForName", context.Background(), mock.Anything).Return(mockDeviceForNameResultFn, - fmt.Errorf("some error")) - - return client -} - -func getProtocols() map[string]contract.ProtocolProperties { - p1 := make(map[string]string) - p1["host"] = "localhost" - p1["port"] = "1234" - p1["unitID"] = "1" - - p2 := make(map[string]string) - p2["serialPort"] = "/dev/USB0" - p2["baudRate"] = "19200" - p2["dataBits"] = "8" - p2["stopBits"] = "1" - p2["parity"] = "0" - p2["unitID"] = "2" - - wrap := make(map[string]contract.ProtocolProperties) - wrap["modbus-ip"] = p1 - wrap["modbus-rtu"] = p2 - - return wrap -} diff --git a/internal/core/data/operators/reading/db.go b/internal/core/data/operators/reading/db.go deleted file mode 100644 index f80b99eb9d..0000000000 --- a/internal/core/data/operators/reading/db.go +++ /dev/null @@ -1,24 +0,0 @@ -/* - * ****************************************************************************** - * Copyright 2019 Dell Inc. - * - * 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 reading - -import contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// Loader provides functionality for loading Readings. -type Loader interface { - ReadingsByValueDescriptor(name string, limit int) ([]contract.Reading, error) -} diff --git a/internal/core/data/operators/reading/get.go b/internal/core/data/operators/reading/get.go deleted file mode 100644 index 78c5068789..0000000000 --- a/internal/core/data/operators/reading/get.go +++ /dev/null @@ -1,66 +0,0 @@ -/* - * ****************************************************************************** - * Copyright 2019 Dell Inc. - * - * 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 reading - -import ( - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -// GetReadingsExecutor retrieves one or more readings. -type GetReadingsExecutor interface { - Execute() ([]contract.Reading, error) -} - -// getReadingsByValueDescriptorName encapsulates the data needed to obtain readings by a value descriptor name. -type getReadingsByValueDescriptorName struct { - name string - limit int - loader Loader - logger logger.LoggingClient - config bootstrapConfig.ServiceInfo -} - -// Execute retrieves readings by value descriptor name. -func (g getReadingsByValueDescriptorName) Execute() ([]contract.Reading, error) { - r, err := g.loader.ReadingsByValueDescriptor(g.name, g.limit) - - if err != nil { - return nil, err - } - - if len(r) > g.config.MaxResultCount { - return nil, errors.NewErrLimitExceeded(len(r)) - } - - return r, nil -} - -// NewGetReadingsNameExecutor creates a GetReadingsExecutor which will retrieve readings by a value descriptor name. -func NewGetReadingsNameExecutor(name string, limit int, loader Loader, logger logger.LoggingClient, config bootstrapConfig.ServiceInfo) GetReadingsExecutor { - return getReadingsByValueDescriptorName{ - name: name, - limit: limit, - loader: loader, - logger: logger, - config: config, - } -} diff --git a/internal/core/data/operators/reading/get_test.go b/internal/core/data/operators/reading/get_test.go deleted file mode 100644 index 7a2b324452..0000000000 --- a/internal/core/data/operators/reading/get_test.go +++ /dev/null @@ -1,164 +0,0 @@ -/* - * ****************************************************************************** - * Copyright 2019 Dell Inc. - * - * 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 reading - -import ( - goErrors "errors" - "reflect" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - "github.com/edgexfoundry/edgex-go/internal/core/data/operators/reading/mocks" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" -) - -// TestErrorReadingName is the name used for testing database errors. -var TestErrorReadingName = "TestReadingError" - -// TestLoaderLimit is the limit to use when invoking the mock Loader. -var TestLoaderLimit = 5 - -// TestError error used to simulate non EdgeX type errors. -var TestError = goErrors.New("testing error") - -var TestReading = contract.Reading{ - Name: "TestReadingName", - Id: "TestReadingID", - Value: "TestReadingValue", - Device: "TestReadingDevice", - Modified: 111, - Created: 222, - Origin: 333, - Pushed: 444, - BinaryValue: []byte{1, 2, 3, 4}, -} - -// Name used for readings which are associated with the same ValueDescriptor -var TestReadingNameForSameValueDescriptor = "TestReadingName2" -var TestReading3 = contract.Reading{ - Name: TestReadingNameForSameValueDescriptor, - Id: "TestReadingID2Dup", - Value: "TestReadingValue2Dup", - Device: "TestReadingDevice2Dup", - Modified: 111, - Created: 222, - Origin: 333, - Pushed: 444, - BinaryValue: []byte{1, 2, 3, 4}, -} - -var TestReading2 = contract.Reading{ - Name: TestReadingNameForSameValueDescriptor, - Id: "TestReadingID2", - Value: "TestReadingValue2", - Device: "TestReadingDevice2", - Modified: 111, - Created: 222, - Origin: 333, - Pushed: 444, - BinaryValue: []byte{1, 2, 3, 4}, -} - -func TestGetReadingsExecutor(t *testing.T) { - tests := []struct { - name string - readingName string - loader Loader - config bootstrapConfig.ServiceInfo - expectedResult []contract.Reading - expectError bool - expectedErrorType error - }{ - { - "Get one Reading", - TestReading.Name, - createMockLoader(), - bootstrapConfig.ServiceInfo{MaxResultCount: 5}, - []contract.Reading{TestReading}, - false, - nil, - }, - - { - "Get multiple Readings", - TestReading2.Name, - createMockLoader(), - bootstrapConfig.ServiceInfo{MaxResultCount: 5}, - []contract.Reading{TestReading2, TestReading3}, - false, - nil, - }, - { - "Database error", - TestErrorReadingName, - createMockLoader(), - bootstrapConfig.ServiceInfo{MaxResultCount: 5}, - nil, - true, - TestError, - }, { - "MaxResultCount exceeded error", - TestReading2.Name, - createMockLoader(), - bootstrapConfig.ServiceInfo{MaxResultCount: 1}, - nil, - true, - errors.ErrLimitExceeded{}, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewGetReadingsNameExecutor(test.readingName, TestLoaderLimit, test.loader, logger.MockLogger{}, test.config) - observed, err := op.Execute() - if test.expectError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectError && test.expectedErrorType != nil { - eet := reflect.TypeOf(test.expectedErrorType) - aet := reflect.TypeOf(err) - if !aet.AssignableTo(eet) { - t.Errorf("Expected error of type %v, but got an error of type %v", eet, aet) - } - } - - if !reflect.DeepEqual(test.expectedResult, observed) { - t.Errorf("Observed result doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedResult, observed) - } - - }) - } -} - -func createMockLoader() Loader { - mock := &mocks.Loader{} - mock.On("ReadingsByValueDescriptor", TestReading.Name, TestLoaderLimit).Return([]contract.Reading{TestReading}, nil) - mock.On("ReadingsByValueDescriptor", TestReading2.Name, TestLoaderLimit).Return([]contract.Reading{TestReading2, TestReading3}, nil) - mock.On("ReadingsByValueDescriptor", TestErrorReadingName, TestLoaderLimit).Return(nil, TestError) - - return mock -} diff --git a/internal/core/data/operators/reading/mocks/Loader.go b/internal/core/data/operators/reading/mocks/Loader.go deleted file mode 100644 index c6a870e0fd..0000000000 --- a/internal/core/data/operators/reading/mocks/Loader.go +++ /dev/null @@ -1,34 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// Loader is an autogenerated mock type for the Loader type -type Loader struct { - mock.Mock -} - -// ReadingsByValueDescriptor provides a mock function with given fields: name, limit -func (_m *Loader) ReadingsByValueDescriptor(name string, limit int) ([]models.Reading, error) { - ret := _m.Called(name, limit) - - var r0 []models.Reading - if rf, ok := ret.Get(0).(func(string, int) []models.Reading); ok { - r0 = rf(name, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Reading) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, int) error); ok { - r1 = rf(name, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/data/operators/value_descriptor/db.go b/internal/core/data/operators/value_descriptor/db.go deleted file mode 100644 index afcce6e65e..0000000000 --- a/internal/core/data/operators/value_descriptor/db.go +++ /dev/null @@ -1,25 +0,0 @@ -/* - * ****************************************************************************** - * Copyright 2019 Dell Inc. - * - * 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 value_descriptor - -import contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// Loader provides functionality for loading ValueDescriptors. -type Loader interface { - ValueDescriptors() ([]contract.ValueDescriptor, error) - ValueDescriptorsByName(names []string) ([]contract.ValueDescriptor, error) -} diff --git a/internal/core/data/operators/value_descriptor/get.go b/internal/core/data/operators/value_descriptor/get.go deleted file mode 100644 index 1cb9d22a28..0000000000 --- a/internal/core/data/operators/value_descriptor/get.go +++ /dev/null @@ -1,93 +0,0 @@ -/* - * ****************************************************************************** - * Copyright 2019 Dell Inc. - * - * 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 value_descriptor - -import ( - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -// GetValueDescriptorsExecutor retrieves one or more value descriptors. -type GetValueDescriptorsExecutor interface { - Execute() ([]contract.ValueDescriptor, error) -} - -// getValueDescriptorsByNames encapsulates data needed to retrieve value descriptors by one or more names. -type getValueDescriptorsByNames struct { - loader Loader - names []string - logger logger.LoggingClient - config bootstrapConfig.ServiceInfo -} - -// Execute retrieves value descriptors by one or more names. -func (g getValueDescriptorsByNames) Execute() ([]contract.ValueDescriptor, error) { - vds, err := g.loader.ValueDescriptorsByName(g.names) - if err != nil { - return nil, err - } - - if len(vds) > g.config.MaxResultCount { - return nil, errors.NewErrLimitExceeded(len(vds)) - } - - return vds, nil -} - -// NewGetValueDescriptorsNameExecutor creates a GetValueDescriptorsExecutor which will get value descriptors matching the provided names. -func NewGetValueDescriptorsNameExecutor(names []string, loader Loader, logger logger.LoggingClient, config bootstrapConfig.ServiceInfo) GetValueDescriptorsExecutor { - return getValueDescriptorsByNames{ - names: names, - logger: logger, - loader: loader, - config: config, - } -} - -// getAllValueDescriptors encapsulates the data needed to retrieve all value descriptors. -type getAllValueDescriptors struct { - loader Loader - logger logger.LoggingClient - config bootstrapConfig.ServiceInfo -} - -// Execute retrieves value descriptors by the provided names. -func (g getAllValueDescriptors) Execute() ([]contract.ValueDescriptor, error) { - vds, err := g.loader.ValueDescriptors() - if err != nil { - return nil, err - } - - if len(vds) > g.config.MaxResultCount { - return nil, errors.NewErrLimitExceeded(len(vds)) - } - - return vds, nil -} - -// NewGetValueDescriptorsExecutor creates a GetValueDescriptorsExecutor which will get all value descriptors. -func NewGetValueDescriptorsExecutor(loader Loader, logger logger.LoggingClient, config bootstrapConfig.ServiceInfo) GetValueDescriptorsExecutor { - return getAllValueDescriptors{ - logger: logger, - loader: loader, - config: config, - } -} diff --git a/internal/core/data/operators/value_descriptor/get_test.go b/internal/core/data/operators/value_descriptor/get_test.go deleted file mode 100644 index 421193f437..0000000000 --- a/internal/core/data/operators/value_descriptor/get_test.go +++ /dev/null @@ -1,227 +0,0 @@ -/* - * ****************************************************************************** - * Copyright 2019 Dell Inc. - * - * 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 value_descriptor - -import ( - goErrors "errors" - "reflect" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - "github.com/edgexfoundry/edgex-go/internal/core/data/operators/value_descriptor/mocks" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -var TestSuccessfulConfig = bootstrapConfig.ServiceInfo{MaxResultCount: 5} -var TestError = goErrors.New("test error") - -var TestVDDescription = "test description" -var TestVDName = "Temperature" -var TestMin = -70 -var TestMax = 140 -var TestUoMLabel = "C" -var TestDefaultValue = 32 -var TestFormatting = "%d" -var TestVDLabels = []string{"temp", "room temp"} -var TestVDFloatEncoding = contract.ENotation -var TestMediaType = "TestMediaType" -var TestValueDescriptor = contract.ValueDescriptor{Created: 123, Modified: 123, Origin: 123, Name: TestVDName, Description: TestVDDescription, Min: TestMin, Max: TestMax, DefaultValue: TestDefaultValue, Formatting: TestFormatting, Labels: TestVDLabels, UomLabel: TestUoMLabel, MediaType: TestMediaType, FloatEncoding: TestVDFloatEncoding} - -// Value descriptors which share the same name. -var TestVDNameSharedName = "TestSharedName" -var TestValueDescriptor2 = contract.ValueDescriptor{Created: 123, Modified: 123, Origin: 123, Name: TestVDNameSharedName, Description: TestVDDescription, Min: TestMin, Max: TestMax, DefaultValue: TestDefaultValue, Formatting: TestFormatting, Labels: TestVDLabels, UomLabel: TestUoMLabel, MediaType: TestMediaType, FloatEncoding: TestVDFloatEncoding} -var TestValueDescriptor3 = contract.ValueDescriptor{Created: 123, Modified: 123, Origin: 123, Name: TestVDNameSharedName, Description: TestVDDescription, Min: TestMin, Max: TestMax, DefaultValue: TestDefaultValue, Formatting: TestFormatting, Labels: TestVDLabels, UomLabel: TestUoMLabel, MediaType: TestMediaType, FloatEncoding: TestVDFloatEncoding} - -var TestVDErrorNames = []string{"TestVDError1", "TestVDError2"} - -func TestGetValueDescriptorsByNames(t *testing.T) { - tests := []struct { - name string - loader Loader - vdNames []string - config bootstrapConfig.ServiceInfo - expectedResult []contract.ValueDescriptor - expectError bool - expectedErrorType error - }{ - { - "Get by one name", - createMockLoader(), - []string{TestVDName}, - TestSuccessfulConfig, - []contract.ValueDescriptor{TestValueDescriptor}, - false, - nil, - }, - { - "Get by multiple names", - createMockLoader(), - []string{TestVDName, TestVDNameSharedName}, - TestSuccessfulConfig, - []contract.ValueDescriptor{TestValueDescriptor, TestValueDescriptor2, TestValueDescriptor3}, - false, - nil, - }, - { - "Database error", - createMockLoader(), - TestVDErrorNames, - TestSuccessfulConfig, - nil, - true, - TestError, - }, - { - "Max result count exceeded error", - createMockLoader(), - []string{TestVDName, TestVDNameSharedName}, - bootstrapConfig.ServiceInfo{MaxResultCount: 1}, - nil, - true, - errors.ErrLimitExceeded{}, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewGetValueDescriptorsNameExecutor(test.vdNames, test.loader, logger.MockLogger{}, test.config) - observed, err := op.Execute() - - if test.expectError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectError && test.expectedErrorType != nil { - eet := reflect.TypeOf(test.expectedErrorType) - aet := reflect.TypeOf(err) - if !aet.AssignableTo(eet) { - t.Errorf("Expected error of type %v, but got an error of type %v", eet, aet) - } - } - - if !reflect.DeepEqual(test.expectedResult, observed) { - t.Errorf("Observed result doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedResult, observed) - } - }) - } -} - -func TestGetAllValueDescriptors(t *testing.T) { - tests := []struct { - name string - loader Loader - config bootstrapConfig.ServiceInfo - expectedResult []contract.ValueDescriptor - expectError bool - expectedErrorType error - }{ - { - "One matching result", - createMockLoaderSingleResult(), - TestSuccessfulConfig, - []contract.ValueDescriptor{TestValueDescriptor}, - false, - nil, - }, - { - "Multiple matching results", - createMockLoader(), - TestSuccessfulConfig, - []contract.ValueDescriptor{TestValueDescriptor, TestValueDescriptor2, TestValueDescriptor3}, - false, - nil, - }, - { - "Database error", - createErrorMockLoader(), - TestSuccessfulConfig, - nil, - true, - TestError, - }, - { - "Max result count exceeded error", - createMockLoader(), - bootstrapConfig.ServiceInfo{MaxResultCount: 1}, - nil, - true, - errors.ErrLimitExceeded{}, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewGetValueDescriptorsExecutor(test.loader, logger.MockLogger{}, test.config) - observed, err := op.Execute() - - if test.expectError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectError && test.expectedErrorType != nil { - eet := reflect.TypeOf(test.expectedErrorType) - aet := reflect.TypeOf(err) - if !aet.AssignableTo(eet) { - t.Errorf("Expected error of type %v, but got an error of type %v", eet, aet) - } - } - - if !reflect.DeepEqual(test.expectedResult, observed) { - t.Errorf("Observed result doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedResult, observed) - } - }) - } -} - -func createMockLoader() Loader { - mock := &mocks.Loader{} - mock.On("ValueDescriptors").Return([]contract.ValueDescriptor{TestValueDescriptor, TestValueDescriptor2, TestValueDescriptor3}, nil) - mock.On("ValueDescriptorsByName", []string{TestVDName}).Return([]contract.ValueDescriptor{TestValueDescriptor}, nil) - mock.On("ValueDescriptorsByName", []string{TestVDNameSharedName}).Return([]contract.ValueDescriptor{TestValueDescriptor2, TestValueDescriptor3}, nil) - mock.On("ValueDescriptorsByName", []string{TestVDName, TestVDNameSharedName}).Return([]contract.ValueDescriptor{TestValueDescriptor, TestValueDescriptor2, TestValueDescriptor3}, nil) - mock.On("ValueDescriptorsByName", TestVDErrorNames).Return(nil, TestError) - - return mock -} - -func createMockLoaderSingleResult() Loader { - mock := &mocks.Loader{} - mock.On("ValueDescriptors").Return([]contract.ValueDescriptor{TestValueDescriptor}, nil) - - return mock -} - -func createErrorMockLoader() Loader { - mock := &mocks.Loader{} - mock.On("ValueDescriptors").Return(nil, TestError) - - return mock -} diff --git a/internal/core/data/operators/value_descriptor/mocks/Loader.go b/internal/core/data/operators/value_descriptor/mocks/Loader.go deleted file mode 100644 index 44cd87c3ca..0000000000 --- a/internal/core/data/operators/value_descriptor/mocks/Loader.go +++ /dev/null @@ -1,57 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// Loader is an autogenerated mock type for the Loader type -type Loader struct { - mock.Mock -} - -// ValueDescriptors provides a mock function with given fields: -func (_m *Loader) ValueDescriptors() ([]models.ValueDescriptor, error) { - ret := _m.Called() - - var r0 []models.ValueDescriptor - if rf, ok := ret.Get(0).(func() []models.ValueDescriptor); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.ValueDescriptor) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ValueDescriptorsByName provides a mock function with given fields: names -func (_m *Loader) ValueDescriptorsByName(names []string) ([]models.ValueDescriptor, error) { - ret := _m.Called(names) - - var r0 []models.ValueDescriptor - if rf, ok := ret.Get(0).(func([]string) []models.ValueDescriptor); ok { - r0 = rf(names) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.ValueDescriptor) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func([]string) error); ok { - r1 = rf(names) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/data/reading.go b/internal/core/data/reading.go deleted file mode 100644 index 9dac2ed5d3..0000000000 --- a/internal/core/data/reading.go +++ /dev/null @@ -1,297 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * - * 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 data - -import ( - "context" - "encoding/json" - "fmt" - "io" - - "github.com/edgexfoundry/edgex-go/internal/core/data/config" - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - "github.com/edgexfoundry/edgex-go/internal/core/data/interfaces" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/metadata" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -func getAllReadings(lc logger.LoggingClient, dbClient interfaces.DBClient, - configuration *config.ConfigurationStruct) (readings []contract.Reading, err error) { - - readings, err = dbClient.Readings() - if err != nil { - lc.Error(err.Error()) - return nil, err - } - - // Check max limit - err = checkMaxLimit(len(readings), lc, configuration) - if err != nil { - return nil, err - } - - return readings, nil -} - -func decodeReading( - reader io.Reader, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - configuration *config.ConfigurationStruct) (reading contract.Reading, err error) { - - reading = contract.Reading{} - err = json.NewDecoder(reader).Decode(&reading) - - // Problem decoding - if err != nil { - lc.Error("Error decoding the reading: " + err.Error()) - - return contract.Reading{}, errors.NewErrJsonDecoding(reading.Name) - } - - if configuration.Writable.ValidateCheck { - err = validateReading(reading, lc, dbClient) - - if err != nil { - return contract.Reading{}, err - } - } - - return reading, nil -} - -func validateReading(reading contract.Reading, lc logger.LoggingClient, dbClient interfaces.DBClient) error { - // Check the value descriptor - vd, err := dbClient.ValueDescriptorByName(reading.Name) - if err != nil { - lc.Error(err.Error()) - if err == db.ErrNotFound { - return errors.NewErrDbNotFound() - } else { - return err - } - } - - err = isValidValueDescriptor(vd, reading) - if err != nil { - lc.Error(err.Error()) - return err - } - - return nil -} - -func addReading( - reading contract.Reading, - lc logger.LoggingClient, - dbClient interfaces.DBClient) (id string, err error) { - - id, err = dbClient.AddReading(reading) - - if err != nil { - lc.Error(err.Error()) - - return "", err - } - - return id, nil -} - -func getReadingById( - id string, - lc logger.LoggingClient, - dbClient interfaces.DBClient) (reading contract.Reading, err error) { - - reading, err = dbClient.ReadingById(id) - - if err != nil { - lc.Error(err.Error()) - if err == db.ErrNotFound { - return contract.Reading{}, errors.NewErrDbNotFound() - } else { - return contract.Reading{}, err - } - } - - return reading, nil -} - -func deleteReadingById(id string, lc logger.LoggingClient, dbClient interfaces.DBClient) error { - err := dbClient.DeleteReadingById(id) - if err != nil { - if err == db.ErrNotFound { - return errors.NewErrDbNotFound() - } - lc.Error(err.Error()) - return err - } - - return nil -} - -func updateReading( - reading contract.Reading, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - configuration *config.ConfigurationStruct) error { - to, err := getReadingById(reading.Id, lc, dbClient) - if err != nil { - return err - } - - // Update the fields - if reading.Value != "" { - to.Value = reading.Value - } - if reading.Name != "" { - to.Name = reading.Name - } - if reading.Origin != 0 { - to.Origin = reading.Origin - } - - if reading.Value != "" || reading.Name != "" { - if configuration.Writable.ValidateCheck { - fmt.Println(to) - - err = validateReading(to, lc, dbClient) - if err != nil { - lc.Error("Error validating updated reading") - return err - } - } - } - - err = dbClient.UpdateReading(reading) - if err != nil { - lc.Error(err.Error()) - return err - } - - return nil -} - -func countReadings(lc logger.LoggingClient, dbClient interfaces.DBClient) (count int, err error) { - count, err = dbClient.ReadingCount() - if err != nil { - lc.Error(err.Error()) - return 0, err - } - - return count, nil -} - -func getReadingsByDevice( - deviceId string, - limit int, - ctx context.Context, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - mdc metadata.DeviceClient, - configuration *config.ConfigurationStruct) (readings []contract.Reading, err error) { - - if checkDevice(deviceId, ctx, mdc, configuration) != nil { - lc.Error(fmt.Sprintf("error checking device %s %v", deviceId, err)) - - return []contract.Reading{}, err - } - - readings, err = dbClient.ReadingsByDevice(deviceId, limit) - if err != nil { - lc.Error(err.Error()) - return []contract.Reading{}, err - } - - return readings, nil -} - -func getReadingsByValueDescriptor( - name string, - limit int, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - configuration *config.ConfigurationStruct) (readings []contract.Reading, err error) { - - // Limit is too large - err = checkMaxLimit(limit, lc, configuration) - if err != nil { - return []contract.Reading{}, err - } - - // Check for value descriptor - if configuration.Writable.ValidateCheck { - _, err = getValueDescriptorByName(name, lc, dbClient) - if err != nil { - return []contract.Reading{}, err - } - } - - readings, err = dbClient.ReadingsByValueDescriptor(name, limit) - if err != nil { - lc.Error(err.Error()) - return []contract.Reading{}, err - } - - return readings, nil -} - -func getReadingsByValueDescriptorNames( - listOfNames []string, - limit int, - lc logger.LoggingClient, - dbClient interfaces.DBClient) (readings []contract.Reading, err error) { - - readings, err = dbClient.ReadingsByValueDescriptorNames(listOfNames, limit) - if err != nil { - lc.Error(err.Error()) - return nil, err - } - - return readings, nil -} - -func getReadingsByCreationTime( - start int64, - end int64, - limit int, - lc logger.LoggingClient, - dbClient interfaces.DBClient) (readings []contract.Reading, err error) { - - readings, err = dbClient.ReadingsByCreationTime(start, end, limit) - if err != nil { - lc.Error(err.Error()) - return nil, err - } - - return readings, nil -} - -func getReadingsByDeviceAndValueDescriptor( - device string, - name string, - limit int, - lc logger.LoggingClient, - dbClient interfaces.DBClient) (readings []contract.Reading, err error) { - - readings, err = dbClient.ReadingsByDeviceAndValueDescriptor(device, name, limit) - if err != nil { - lc.Error(err.Error()) - return nil, err - } - - return readings, nil -} diff --git a/internal/core/data/reading_test.go b/internal/core/data/reading_test.go deleted file mode 100644 index 96b07e59f2..0000000000 --- a/internal/core/data/reading_test.go +++ /dev/null @@ -1,295 +0,0 @@ -package data - -import ( - "context" - "fmt" - "math" - "testing" - - dataConfig "github.com/edgexfoundry/edgex-go/internal/core/data/config" - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - "github.com/edgexfoundry/edgex-go/internal/core/data/interfaces" - dbMock "github.com/edgexfoundry/edgex-go/internal/core/data/interfaces/mocks" - dataMocks "github.com/edgexfoundry/edgex-go/internal/core/data/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/stretchr/testify/mock" -) - -func newReadingsMockDB() interfaces.DBClient { - db := &dbMock.DBClient{} - - db.On("Readings").Return(buildReadings(), nil) - - return db -} - -func TestGetAllReadings(t *testing.T) { - reset() - _, err := getAllReadings( - logger.NewMockClient(), - newReadingsMockDB(), - &dataConfig.ConfigurationStruct{ - Service: config.ServiceInfo{ - MaxResultCount: 5, - }, - }) - - if err != nil { - t.Errorf("Unexpected error thrown getting all readings: %s", err.Error()) - } -} - -func TestGetAllReadingsOverLimit(t *testing.T) { - reset() - _, err := getAllReadings(logger.NewMockClient(), newReadingsMockDB(), &dataConfig.ConfigurationStruct{ - Service: config.ServiceInfo{ - MaxResultCount: 1, - }}) - - if err != nil { - switch err.(type) { - case errors.ErrLimitExceeded: - return - default: - t.Errorf("Unexpected error getting all readings") - } - } - - if err == nil { - t.Errorf("Expected error getting all readings") - } -} - -func TestGetAllReadingsError(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("Readings").Return([]models.Reading{}, fmt.Errorf("some error")) - _, err := getAllReadings(logger.NewMockClient(), dbClientMock, &dataConfig.ConfigurationStruct{ - Service: config.ServiceInfo{ - MaxResultCount: 5, - }}) - - if err == nil { - t.Errorf("Expected error getting all readings") - } -} - -func TestAddReading(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("AddReading", mock.Anything).Return("", nil) - _, err := addReading(models.Reading{Name: "valid"}, logger.NewMockClient(), dbClientMock) - if err != nil { - t.Errorf("Unexpected error adding reading") - } -} - -func TestAddReadingError(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("AddReading", mock.Anything).Return("", fmt.Errorf("some error")) - _, err := addReading(models.Reading{}, logger.NewMockClient(), dbClientMock) - if err == nil { - t.Errorf("Expected error adding reading") - } -} - -func TestGetReadingById(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("ReadingById", mock.Anything).Return(models.Reading{}, nil) - _, err := getReadingById("valid", logger.NewMockClient(), dbClientMock) - if err != nil { - t.Errorf("Unexpected error getting reading by ID") - } -} - -func TestGetReadingByIdNotFound(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("ReadingById", mock.Anything).Return(models.Reading{}, db.ErrNotFound) - _, err := getReadingById("404", logger.NewMockClient(), dbClientMock) - if err != nil { - switch err.(type) { - case errors.ErrDbNotFound: - return - default: - t.Errorf("Unexpected error getting reading by ID missing in DB") - } - } - - if err == nil { - t.Errorf("Expected error getting reading by ID missing in DB") - } -} - -func TestGetReadingByIdError(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("ReadingById", mock.Anything).Return(models.Reading{}, fmt.Errorf("some error")) - _, err := getReadingById("error", logger.NewMockClient(), dbClientMock) - if err == nil { - t.Errorf("Expected error getting reading by ID with some error") - } -} - -func TestDeleteReadingById(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("DeleteReadingById", mock.Anything).Return(nil).Once() - err := deleteReadingById("valid", logger.NewMockClient(), dbClientMock) - if err != nil { - t.Errorf("Unexpected error deleting reading by ID") - } - - dbClientMock.AssertExpectations(t) -} - -func TestDeleteReadingByIdError(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("DeleteReadingById", mock.Anything).Return(fmt.Errorf("some error")) - err := deleteReadingById("invalid", logger.NewMockClient(), dbClientMock) - if err == nil { - t.Errorf("Expected error deleting reading by ID") - } - - dbClientMock.AssertExpectations(t) -} - -func TestCountReadings(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("ReadingCount").Return(2, nil) - _, err := countReadings(logger.NewMockClient(), dbClientMock) - if err != nil { - t.Errorf("Unexpected error in CountReadings") - } -} - -func TestCountReadingsError(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("ReadingCount").Return(2, fmt.Errorf("some error")) - _, err := countReadings(logger.NewMockClient(), dbClientMock) - if err == nil { - t.Errorf("Expected error in CountReadings") - } -} - -func TestGetReadingsByDevice(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("ReadingsByDevice", mock.Anything, mock.Anything).Return(buildReadings(), nil) - expectedReadings, err := getReadingsByDevice("valid", 0, context.Background(), logger.NewMockClient(), dbClientMock, dataMocks.NewMockDeviceClient(), &dataConfig.ConfigurationStruct{}) - if err != nil { - t.Errorf("Unexpected error in getReadingsByDevice") - } - - if len(buildReadings()) != len(expectedReadings) { - t.Errorf("Found %d readings, expected %d", len(expectedReadings), len(buildReadings())) - } -} - -func TestGetReadingsByDeviceError(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("ReadingsByDevice", mock.Anything, mock.Anything).Return([]models.Reading{}, fmt.Errorf("some error")) - _, err := getReadingsByDevice("error", 0, context.Background(), logger.NewMockClient(), dbClientMock, dataMocks.NewMockDeviceClient(), &dataConfig.ConfigurationStruct{}) - if err == nil { - t.Errorf("Expected error in getReadingsByDevice") - } -} - -func TestGetReadingsByValueDescriptor(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("ReadingsByValueDescriptor", mock.Anything, mock.Anything).Return([]models.Reading{}, nil) - _, err := getReadingsByValueDescriptor("valid", 0, logger.NewMockClient(), dbClientMock, &dataConfig.ConfigurationStruct{}) - if err != nil { - t.Errorf("Unexpected error getting readings by value descriptor") - } -} - -func TestGetReadingsByValueDescriptorOverLimit(t *testing.T) { - reset() - _, err := getReadingsByValueDescriptor("", math.MaxInt32, logger.NewMockClient(), nil, &dataConfig.ConfigurationStruct{}) - if err == nil { - t.Errorf("Expected error getting readings by value descriptor") - } -} - -func TestGetReadingsByValueDescriptorError(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("ReadingsByValueDescriptor", mock.Anything, mock.Anything).Return([]models.Reading{}, fmt.Errorf("some error")) - _, err := getReadingsByValueDescriptor("error", 0, logger.NewMockClient(), dbClientMock, &dataConfig.ConfigurationStruct{}) - if err == nil { - t.Errorf("Expected error in getting readings by value descriptor") - } -} - -func TestGetReadingsByValueDescriptorNames(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("ReadingsByValueDescriptorNames", mock.Anything, mock.Anything).Return([]models.Reading{}, nil) - _, err := getReadingsByValueDescriptorNames([]string{"valid"}, 0, logger.NewMockClient(), dbClientMock) - if err != nil { - t.Errorf("Unexpected error getting readings by value descriptor names") - } -} - -func TestGetReadingsByValueDescriptorNamesError(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("ReadingsByValueDescriptorNames", mock.Anything, mock.Anything).Return([]models.Reading{}, fmt.Errorf("some error")) - _, err := getReadingsByValueDescriptorNames([]string{"error"}, 0, logger.NewMockClient(), dbClientMock) - if err == nil { - t.Errorf("Expected error in getting readings by value descriptor names") - } -} - -func TestGetReadingsByCreationTime(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("ReadingsByCreationTime", mock.Anything, mock.Anything, mock.Anything).Return([]models.Reading{}, nil) - _, err := getReadingsByCreationTime(0xBEEF, 0, 0, logger.NewMockClient(), dbClientMock) - if err != nil { - t.Errorf("Unexpected error getting readings by creation time") - } -} - -func TestGetReadingsByCreationTimeError(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("ReadingsByCreationTime", mock.Anything, mock.Anything, mock.Anything).Return([]models.Reading{}, fmt.Errorf("some error")) - _, err := getReadingsByCreationTime(0xDEADBEEF, 0, 0, logger.NewMockClient(), dbClientMock) - if err == nil { - t.Errorf("Expected error in getting readings by creation time") - } -} - -func TestGetReadingsByDeviceAndValueDescriptor(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("ReadingsByDeviceAndValueDescriptor", mock.Anything, mock.Anything, mock.Anything).Return([]models.Reading{}, nil) - _, err := getReadingsByDeviceAndValueDescriptor("valid", "valid", 0, logger.NewMockClient(), dbClientMock) - if err != nil { - t.Errorf("Unexpected error getting readings by device and value descriptor") - } -} - -func TestGetReadingsByDeviceAndValueDescriptorError(t *testing.T) { - reset() - dbClientMock := &dbMock.DBClient{} - dbClientMock.On("ReadingsByDeviceAndValueDescriptor", mock.Anything, mock.Anything, mock.Anything).Return([]models.Reading{}, fmt.Errorf("some error")) - _, err := getReadingsByDeviceAndValueDescriptor("error", "error", 0, logger.NewMockClient(), dbClientMock) - if err == nil { - t.Errorf("Expected error in getting readings by device and value descriptor") - } -} diff --git a/internal/core/data/router.go b/internal/core/data/router.go deleted file mode 100644 index 9341d66dfa..0000000000 --- a/internal/core/data/router.go +++ /dev/null @@ -1,2029 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * - * 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 data - -import ( - "encoding/json" - "net/http" - "net/url" - "strconv" - "strings" - - "github.com/edgexfoundry/edgex-go/internal/core/data/config" - dataContainer "github.com/edgexfoundry/edgex-go/internal/core/data/container" - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - "github.com/edgexfoundry/edgex-go/internal/core/data/interfaces" - readingOperator "github.com/edgexfoundry/edgex-go/internal/core/data/operators/reading" - "github.com/edgexfoundry/edgex-go/internal/core/data/operators/value_descriptor" - "github.com/edgexfoundry/edgex-go/internal/pkg" - "github.com/edgexfoundry/edgex-go/internal/pkg/bootstrap/container" - errorContainer "github.com/edgexfoundry/edgex-go/internal/pkg/container" - "github.com/edgexfoundry/edgex-go/internal/pkg/correlation" - "github.com/edgexfoundry/edgex-go/internal/pkg/correlation/models" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - "github.com/edgexfoundry/edgex-go/internal/pkg/telemetry" - - bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/container" - "github.com/edgexfoundry/go-mod-bootstrap/v2/di" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/metadata" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/edgexfoundry/go-mod-messaging/v2/messaging" - - "github.com/gorilla/mux" -) - -// ValueDescriptorUsageReadLimit limit of readings obtained for a given value descriptor to determine if the value -// descriptor is in use. -var ValueDescriptorUsageReadLimit = 1 - -func loadRestRoutes(r *mux.Router, dic *di.Container) { - // Ping Resource - r.HandleFunc( - clients.ApiPingRoute, - func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set(clients.ContentType, clients.ContentTypeText) - _, _ = w.Write([]byte("pong")) - }).Methods(http.MethodGet) - - // Configuration - r.HandleFunc( - clients.ApiConfigRoute, - func(w http.ResponseWriter, _ *http.Request) { - pkg.Encode(dataContainer.ConfigurationFrom(dic.Get), w, bootstrapContainer.LoggingClientFrom(dic.Get)) - }).Methods(http.MethodGet) - - // Metrics - r.HandleFunc( - clients.ApiMetricsRoute, - func(w http.ResponseWriter, _ *http.Request) { - pkg.Encode(telemetry.NewSystemUsage(), w, bootstrapContainer.LoggingClientFrom(dic.Get)) - }).Methods(http.MethodGet) - - // Version - r.HandleFunc(clients.ApiVersionRoute, pkg.VersionHandler).Methods(http.MethodGet) - - // Events - r.HandleFunc( - clients.ApiEventRoute, - func(w http.ResponseWriter, r *http.Request) { - eventHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - dataContainer.PublisherEventsChannelFrom(dic.Get), - dataContainer.MessagingClientFrom(dic.Get), - dataContainer.MetadataDeviceClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet, http.MethodPut, http.MethodPost) - r.HandleFunc(clients.ApiEventRoute, func(writer http.ResponseWriter, request *http.Request) { - eventHandler( - writer, - request, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - dataContainer.PublisherEventsChannelFrom(dic.Get), - dataContainer.MessagingClientFrom(dic.Get), - dataContainer.MetadataDeviceClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet, http.MethodPut, http.MethodPost) - - e := r.PathPrefix(clients.ApiEventRoute).Subrouter() - - e.HandleFunc( - "/"+SCRUB, - func(w http.ResponseWriter, r *http.Request) { - scrubHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodDelete) - - e.HandleFunc( - "/"+SCRUBALL, - func(w http.ResponseWriter, r *http.Request) { - scrubAllHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodDelete) - - e.HandleFunc( - "/"+COUNT, - func(w http.ResponseWriter, r *http.Request) { - eventCountHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - e.HandleFunc( - "/"+COUNT+"/{"+DEVICEID_PARAM+"}", - func(w http.ResponseWriter, r *http.Request) { - eventCountByDeviceIdHandler( - w, - r, - container.DBClientFrom(dic.Get), - dataContainer.MetadataDeviceClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - - e.HandleFunc( - "/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - getEventByIdHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - e.HandleFunc( - "/"+ID+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - eventIdHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - dataContainer.MetadataDeviceClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodDelete, http.MethodPut) - - e.HandleFunc( - "/"+CHECKSUM+"/{"+CHECKSUM+"}", - func(w http.ResponseWriter, r *http.Request) { - putEventChecksumHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - dataContainer.MetadataDeviceClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPut) - - e.HandleFunc( - "/"+DEVICE+"/{"+DEVICEID_PARAM+"}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - getEventByDeviceHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - dataContainer.MetadataDeviceClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - e.HandleFunc( - "/"+DEVICE+"/{"+DEVICEID_PARAM+"}", - func(w http.ResponseWriter, r *http.Request) { - deleteByDeviceIdHandler( - w, - r, - container.DBClientFrom(dic.Get), - dataContainer.MetadataDeviceClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodDelete) - - e.HandleFunc( - "/"+REMOVEOLD+"/"+AGE+"/{"+AGE+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - eventByAgeHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodDelete) - - e.HandleFunc( - "/{"+START+":[0-9]+}/{"+END+":[0-9]+}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - eventByCreationTimeHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - - // Readings - r.HandleFunc( - clients.ApiReadingRoute, - func(w http.ResponseWriter, r *http.Request) { - readingHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - dataContainer.MetadataDeviceClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet, http.MethodPut, http.MethodPost) - - rd := r.PathPrefix(clients.ApiReadingRoute).Subrouter() - - rd.HandleFunc( - "/"+COUNT, - func(w http.ResponseWriter, r *http.Request) { - readingCountHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - rd.HandleFunc( - "/"+ID+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - deleteReadingByIdHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodDelete) - - rd.HandleFunc( - "/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - getReadingByIdHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - rd.HandleFunc( - "/"+DEVICE+"/{"+DEVICEID_PARAM+"}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - readingByDeviceHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - dataContainer.MetadataDeviceClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - - rd.HandleFunc( - "/"+NAME+"/{"+NAME+"}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - readingbyValueDescriptorHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - - rd.HandleFunc( - "/"+UOMLABEL+"/{"+UOMLABEL_PARAM+"}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - readingByUomLabelHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - - rd.HandleFunc( - "/"+LABEL+"/{"+LABEL+"}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - readingByLabelHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - - rd.HandleFunc( - "/"+TYPE+"/{"+TYPE+"}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - readingByTypeHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - - rd.HandleFunc( - "/{"+START+":[0-9]+}/{"+END+":[0-9]+}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - readingByCreationTimeHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - - rd.HandleFunc( - "/"+NAME+"/{"+NAME+"}/"+DEVICE+"/{"+DEVICE+"}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - readingByValueDescriptorAndDeviceHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - dataContainer.MetadataDeviceClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - - // Value descriptors - r.HandleFunc( - clients.ApiValueDescriptorRoute, - func(w http.ResponseWriter, r *http.Request) { - valueDescriptorHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet, http.MethodPut, http.MethodPost) - - vd := r.PathPrefix(clients.ApiValueDescriptorRoute).Subrouter() - - vd.HandleFunc( - "/"+USAGE, - func(w http.ResponseWriter, r *http.Request) { - restValueDescriptorsUsageHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - dataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - - vd.HandleFunc( - "/"+ID+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - deleteValueDescriptorByIdHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodDelete) - - vd.HandleFunc( - "/"+NAME+"/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - valueDescriptorByNameHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet, http.MethodDelete) - - vd.HandleFunc( - "/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - valueDescriptorByIdHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - vd.HandleFunc( - "/"+UOMLABEL+"/{"+UOMLABEL_PARAM+"}", - func(w http.ResponseWriter, r *http.Request) { - valueDescriptorByUomLabelHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - vd.HandleFunc( - "/"+LABEL+"/{"+LABEL+"}", - func(w http.ResponseWriter, r *http.Request) { - valueDescriptorByLabelHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - vd.HandleFunc( - "/"+DEVICENAME+"/{"+DEVICE+"}", - func(w http.ResponseWriter, r *http.Request) { - valueDescriptorByDeviceHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - dataContainer.MetadataDeviceClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - vd.HandleFunc( - "/"+DEVICEID+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - valueDescriptorByDeviceIdHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - dataContainer.MetadataDeviceClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - r.Use(correlation.ManageHeader) - r.Use(correlation.LoggingMiddleware(bootstrapContainer.LoggingClientFrom(dic.Get))) -} - -/* -Return number of events in Core Data -/api/v1/event/count -*/ -func eventCountHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler) { - - defer func() { _ = r.Body.Close() }() - - count, err := countEvents(dbClient) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - // Return result - w.WriteHeader(http.StatusOK) - _, err = w.Write([]byte(strconv.Itoa(count))) - if err != nil { - lc.Error(err.Error()) - } -} - -/* -Return number of events for a given device in Core Data -deviceID - ID of the device to get count for -/api/v1/event/count/{deviceId} -*/ -func eventCountByDeviceIdHandler( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - mdc metadata.DeviceClient, - httpErrorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - id, err := url.QueryUnescape(vars["deviceId"]) - ctx := r.Context() - - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check device - count, err := countEventsByDevice(id, ctx, dbClient, mdc, configuration) - if err != nil { - httpErrorHandler.HandleOneVariant(w, - err, - errorconcept.NewServiceClientHttpError(err), - errorconcept.Default.InternalServerError) - } - - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(strconv.Itoa(count))) -} - -// Remove all the old events and associated readings (by age) -// event/removeold/age/{age} -func eventByAgeHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - age, err := strconv.ParseInt(vars["age"], 10, 64) - - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - lc.Info("Deleting events by age: " + vars["age"]) - - count, err := deleteEventsByAge(age, lc, dbClient) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(strconv.Itoa(count))) -} - -/* -Handler for the event API -Status code 400 - Unsupported content type, or invalid data -Status code 404 - event not found -Status code 413 - number of events exceeds limit -Status code 500 - unanticipated issues -api/v1/event -*/ -func eventHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - chEvents chan<- interface{}, - msgClient messaging.MessageClient, - mdc metadata.DeviceClient, - httpErrorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - if r.Body != nil { - defer func() { _ = r.Body.Close() }() - } - - ctx := r.Context() - - switch r.Method { - // Get all events - case http.MethodGet: - events, err := getEvents(configuration.Service.MaxResultCount, dbClient) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - pkg.Encode(events, w, lc) - break - // Post a new event - case http.MethodPost: - reader := NewRequestReader(r, configuration) - - evt := models.Event{} - evt, err := reader.Read(r.Body, &ctx) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - newId, err := addNewEvent(evt, ctx, lc, dbClient, chEvents, msgClient, mdc, configuration) - if err != nil { - httpErrorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.ValueDescriptors.NotFound, - errorconcept.ValueDescriptors.Invalid, - errorconcept.NewServiceClientHttpError(err), - }, - errorconcept.Default.InternalServerError) - return - } - - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(newId)) - break - // Update an existing event, but do not update the readings - case http.MethodPut: - contentType := r.Header.Get(clients.ContentType) - if contentType == clients.ContentTypeCBOR { - httpErrorHandler.Handle(w, errors.ErrCBORNotSupported{}, errorconcept.CBOR.NotSupported) - return - } - - var from models.Event - dec := json.NewDecoder(r.Body) - err := dec.Decode(&from) - - // Problem decoding event - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - lc.Info("Updating event: " + from.ID) - err = updateEvent(from, ctx, dbClient, mdc, configuration) - if err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.Events.NotFound, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("true")) - } -} - -// Undocumented feature to remove all readings and events from the database -// This should primarily be used for debugging purposes -func scrubAllHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler) { - - defer func() { _ = r.Body.Close() }() - - lc.Info("Deleting all events from database") - - err := deleteAllEvents(dbClient) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - pkg.Encode(true, w, lc) -} - -// GET -// Return the event specified by the event ID -// /api/v1/event/{id} -// id - ID of the event to return -func getEventByIdHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler) { - - if r.Body != nil { - defer func() { _ = r.Body.Close() }() - } - - // URL parameters - vars := mux.Vars(r) - id := vars["id"] - - // Get the event - e, err := getEventById(id, dbClient) - if err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.Events.NotFound, - errorconcept.Default.InternalServerError) - return - } - - pkg.Encode(e, w, lc) -} - -// Get event by device id -// Returns the events for the given device sorted by creation date and limited by 'limit' -// {deviceId} - the device that the events are for -// {limit} - the limit of events -// api/v1/event/device/{deviceId}/{limit} -func getEventByDeviceHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - mdc metadata.DeviceClient, - httpErrorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - limit := vars["limit"] - ctx := r.Context() - deviceId, err := url.QueryUnescape(vars["deviceId"]) - - // Problems unescaping URL - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Convert limit to int - limitNum, err := strconv.Atoi(limit) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check device - if err := checkDevice(deviceId, ctx, mdc, configuration); err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.NewServiceClientHttpError(err), - errorconcept.Default.ServiceUnavailable) - } - - switch r.Method { - case http.MethodGet: - err := checkMaxLimit(limitNum, lc, configuration) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.LimitExceeded) - return - } - - eventList, err := getEventsByDeviceIdLimit(limitNum, deviceId, lc, dbClient) - - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - pkg.Encode(eventList, w, lc) - } -} - -/* -DELETE, PUT -Handle events specified by an ID -/api/v1/event/id/{id} -404 - ID not found -*/ -func eventIdHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - mdc metadata.DeviceClient, - httpErrorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - id := vars["id"] - ctx := r.Context() - - switch r.Method { - // Set the 'pushed' timestamp for the event to the current time - event is going to another (not EdgeX) service - case http.MethodPut: - contentType := r.Header.Get(clients.ContentType) - if contentType == clients.ContentTypeCBOR { - httpErrorHandler.Handle(w, errors.ErrCBORNotSupported{}, errorconcept.CBOR.NotSupported) - return - } - - lc.Info("Updating event: " + id) - - err := updateEventPushDate(id, ctx, dbClient, mdc, configuration) - if err != nil { - httpErrorHandler.HandleOneVariant(w, - err, - errorconcept.Events.NotFound, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("true")) - break - // Delete the event and all of it's readings - case http.MethodDelete: - lc.Info("Deleting event: " + id) - err := deleteEventById(id, lc, dbClient) - if err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.Events.NotFound, - errorconcept.Default.InternalServerError) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("true")) - } -} - -/* -PUT -Handle events specified by a Checksum -/api/v1/event/checksum/{checksum} -404 - ID not found -*/ -func putEventChecksumHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - mdc metadata.DeviceClient, - httpErrorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - checksum := vars["checksum"] - ctx := r.Context() - - switch r.Method { - // Set the 'pushed' timestamp for the event to the current time - event is going to another (not EdgeX) service - case http.MethodPut: - lc.Debug("Updating event with checksum: " + checksum) - - err := updateEventPushDateByChecksum(checksum, ctx, dbClient, mdc, configuration) - if err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.Database.NotFound, - errorconcept.Default.InternalServerError) - return - } - - w.WriteHeader(http.StatusOK) - break - } -} - -// Delete all of the events associated with a device -// api/v1/event/device/{deviceId} -// 404 - device ID not found in metadata -// 503 - service unavailable -func deleteByDeviceIdHandler( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - mdc metadata.DeviceClient, - httpErrorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - deviceId, err := url.QueryUnescape(vars["deviceId"]) - ctx := r.Context() - - // Problems unescaping URL - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check device - if err := checkDevice(deviceId, ctx, mdc, configuration); err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.NewServiceClientHttpError(err), - errorconcept.Default.InternalServerError) - } - - switch r.Method { - case http.MethodDelete: - count, err := deleteEvents(deviceId, dbClient) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(strconv.Itoa(count))) - } -} - -// Get events by creation time -// {start} - start time, {end} - end time, {limit} - max number of results -// Sort the events by creation date -// 413 - number of results exceeds limit -// 503 - service unavailable -// api/v1/event/{start}/{end}/{limit} -func eventByCreationTimeHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - start, err := strconv.ParseInt(vars["start"], 10, 64) - // Problems converting start time - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - end, err := strconv.ParseInt(vars["end"], 10, 64) - // Problems converting end time - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - limit, err := strconv.Atoi(vars["limit"]) - // Problems converting limit - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - switch r.Method { - case http.MethodGet: - err := checkMaxLimit(limit, lc, configuration) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.LimitExceeded) - return - } - - eventList, err := getEventsByCreationTime(limit, start, end, lc, dbClient) - - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - pkg.Encode(eventList, w, lc) - } -} - -// Scrub all the events that have been pushed -// Also remove the readings associated with the events -// api/v1/event/scrub -func scrubHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler) { - - defer func() { _ = r.Body.Close() }() - - switch r.Method { - case http.MethodDelete: - count, err := scrubPushedEvents(lc, dbClient) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(strconv.Itoa(count))) - } -} - -// Reading handler -// GET, PUT, and POST readings -func readingHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - mdc metadata.DeviceClient, - httpErrorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - defer func() { _ = r.Body.Close() }() - - ctx := r.Context() - - switch r.Method { - case http.MethodGet: - r, err := getAllReadings(lc, dbClient, configuration) - - if err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.LimitExceeded, - errorconcept.Default.InternalServerError) - } - - pkg.Encode(r, w, lc) - case http.MethodPost: - reading, err := decodeReading(r.Body, lc, dbClient, configuration) - - // Problem decoding - if err != nil { - httpErrorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.Common.JsonDecoding, - errorconcept.ValueDescriptors.NotFoundInDB, - errorconcept.ValueDescriptors.Invalid, - }, - errorconcept.Default.InternalServerError) - } - - // Check device - if reading.Device != "" { - if err := checkDevice(reading.Device, ctx, mdc, configuration); err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.NewServiceClientHttpError(err), - errorconcept.Default.InternalServerError) - } - } - - if configuration.Writable.PersistData { - id, err := addReading(reading, lc, dbClient) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(id)) - } else { - // Didn't save the readingOperator in the database - pkg.Encode("unsaved", w, lc) - } - case http.MethodPut: - from, err := decodeReading(r.Body, lc, dbClient, configuration) - // Problem decoding - if err != nil { - httpErrorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.Common.JsonDecoding, - errorconcept.ValueDescriptors.NotFoundInDB, - errorconcept.ValueDescriptors.Invalid, - }, - errorconcept.Default.InternalServerError) - return - } - - err = updateReading(from, lc, dbClient, configuration) - if err != nil { - httpErrorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.Database.NotFoundTyped, - errorconcept.ValueDescriptors.Invalid, - }, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("true")) - } -} - -// Get a readingOperator by id -// HTTP 404 not found if the readingOperator can't be found by the ID -// api/v1/readingOperator/{id} -func getReadingByIdHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - id := vars["id"] - - switch r.Method { - case http.MethodGet: - reading, err := getReadingById(id, lc, dbClient) - if err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.Database.NotFoundTyped, - errorconcept.Default.InternalServerError) - } - - pkg.Encode(reading, w, lc) - } -} - -// Return a count for the number of readings in core data -// api/v1/readingOperator/count -func readingCountHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler) { - - defer func() { _ = r.Body.Close() }() - - switch r.Method { - case http.MethodGet: - count, err := countReadings(lc, dbClient) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - w.WriteHeader(http.StatusOK) - _, err = w.Write([]byte(strconv.Itoa(count))) - if err != nil { - lc.Error(err.Error()) - } - } -} - -// Delete a readingOperator by its id -// api/v1/readingOperator/id/{id} -func deleteReadingByIdHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - id := vars["id"] - - switch r.Method { - case http.MethodDelete: - err := deleteReadingById(id, lc, dbClient) - if err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.Database.NotFoundTyped, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("true")) - } -} - -// Get all the readings for the device - sort by creation date -// 404 - device ID or name doesn't match -// 413 - max count exceeded -// api/v1/readingOperator/device/{deviceId}/{limit} -func readingByDeviceHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - mdc metadata.DeviceClient, - httpErrorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - limit, err := strconv.Atoi(vars["limit"]) - // Problems converting limit to int - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - deviceId, err := url.QueryUnescape(vars["deviceId"]) - // Problems unescaping URL - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - ctx := r.Context() - - switch r.Method { - case http.MethodGet: - err := checkMaxLimit(limit, lc, configuration) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.LimitExceeded) - return - } - - readings, err := getReadingsByDevice(deviceId, limit, ctx, lc, dbClient, mdc, configuration) - if err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.NewServiceClientHttpError(err), - errorconcept.Default.InternalServerError) - return - } - - pkg.Encode(readings, w, lc) - } -} - -// Return a list of readings associated with a value descriptor, limited by limit -// HTTP 413 (limit exceeded) if the limit is greater than max limit -// api/v1/readingOperator/name/{name}/{limit} -func readingbyValueDescriptorHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - name, err := url.QueryUnescape(vars["name"]) - // Problems with unescaping URL - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - limit, err := strconv.Atoi(vars["limit"]) - // Problems converting limit to int - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - read, err := getReadingsByValueDescriptor(name, limit, lc, dbClient, configuration) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - pkg.Encode(read, w, lc) -} - -// Return a list of readings based on the UOM label for the value decriptor -// api/v1/readingOperator/uomlabel/{uomLabel}/{limit} -func readingByUomLabelHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - - uomLabel, err := url.QueryUnescape(vars["uomLabel"]) - // Problems unescaping URL - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - limit, err := strconv.Atoi(vars["limit"]) - // Problems converting limit to int - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Limit was exceeded - err = checkMaxLimit(limit, lc, configuration) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.LimitExceeded) - return - } - - // Get the value descriptors - vList, err := getValueDescriptorsByUomLabel(uomLabel, lc, dbClient) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - var vNames []string - for _, v := range vList { - vNames = append(vNames, v.Name) - } - - readings, err := getReadingsByValueDescriptorNames(vNames, limit, lc, dbClient) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - pkg.Encode(readings, w, lc) -} - -// Get readings by the value descriptor (specified by the label) -// 413 - limit exceeded -// api/v1/readingOperator/label/{label}/{limit} -func readingByLabelHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - label, err := url.QueryUnescape(vars["label"]) - // Problem unescaping - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - limit, err := strconv.Atoi(vars["limit"]) - // Problems converting to int - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Limit is too large - err = checkMaxLimit(limit, lc, configuration) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.LimitExceeded) - return - } - - // Get the value descriptors - vdList, err := getValueDescriptorsByLabel(label, lc, dbClient) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - var vdNames []string - for _, vd := range vdList { - vdNames = append(vdNames, vd.Name) - } - - readings, err := getReadingsByValueDescriptorNames(vdNames, limit, lc, dbClient) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - pkg.Encode(readings, w, lc) -} - -// Return a list of readings who's value descriptor has the type -// 413 - number exceeds the current limit -// /readingOperator/type/{type}/{limit} -func readingByTypeHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - - t, err := url.QueryUnescape(vars["type"]) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - limit, err := strconv.Atoi(vars["limit"]) - // Problem converting to int - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - err = checkMaxLimit(limit, lc, configuration) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.LimitExceeded) - return - } - - // Get the value descriptors - vdList, err := getValueDescriptorsByType(t, lc, dbClient) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - var vdNames []string - for _, vd := range vdList { - vdNames = append(vdNames, vd.Name) - } - - readings, err := getReadingsByValueDescriptorNames(vdNames, limit, lc, dbClient) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - pkg.Encode(readings, w, lc) -} - -// Return a list of readings between the start and end (creation time) -// /readingOperator/{start}/{end}/{limit} -func readingByCreationTimeHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - start, err := strconv.ParseInt(vars["start"], 10, 64) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - end, err := strconv.ParseInt(vars["end"], 10, 64) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - limit, err := strconv.Atoi(vars["limit"]) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - switch r.Method { - case http.MethodGet: - err = checkMaxLimit(limit, lc, configuration) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.LimitExceeded) - return - } - - readings, err := getReadingsByCreationTime(start, end, limit, lc, dbClient) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - pkg.Encode(readings, w, lc) - } -} - -// Return a list of redings associated with the device and value descriptor -// Limit exceeded exception 413 if the limit exceeds the max limit -// api/v1/readingOperator/name/{name}/device/{device}/{limit} -func readingByValueDescriptorAndDeviceHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - mdc metadata.DeviceClient, - httpErrorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - ctx := r.Context() - - // Get the variables from the URL - name, err := url.QueryUnescape(vars["name"]) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - device, err := url.QueryUnescape(vars["device"]) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - limit, err := strconv.Atoi(vars["limit"]) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - err = checkMaxLimit(limit, lc, configuration) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.LimitExceeded) - return - } - - // Check device - if err := checkDevice(device, ctx, mdc, configuration); err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.NewServiceClientHttpError(err), - errorconcept.Default.InternalServerError) - return - } - - // Check for value descriptor - if configuration.Writable.ValidateCheck { - _, err = getValueDescriptorByName(name, lc, dbClient) - if err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.ValueDescriptors.NotFoundInDB, - errorconcept.Default.InternalServerError) - return - } - } - - readings, err := getReadingsByDeviceAndValueDescriptor(device, name, limit, lc, dbClient) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - pkg.Encode(readings, w, lc) -} - -// Value Descriptors - -// GET, POST, and PUT for value descriptors -// api/v1/valuedescriptor -func valueDescriptorHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - defer func() { _ = r.Body.Close() }() - - switch r.Method { - case http.MethodGet: - vList, err := getAllValueDescriptors(lc, dbClient) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - // Check the limit - err = checkMaxLimit(len(vList), lc, configuration) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.LimitExceeded) - return - } - - pkg.Encode(vList, w, lc) - case http.MethodPost: - v, err := decodeValueDescriptor(r.Body, lc) - if err != nil { - httpErrorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.Common.JsonDecoding, - errorconcept.ValueDescriptors.Invalid, - }, - errorconcept.Default.InternalServerError) - return - } - - id, err := addValueDescriptor(v, lc, dbClient) - if err != nil { - httpErrorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.ValueDescriptors.SingleInUse, - errorconcept.ValueDescriptors.MultipleInUse, - errorconcept.ValueDescriptors.DuplicateName, - }, - errorconcept.Default.InternalServerError) - return - } - - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(id)) - case http.MethodPut: - vd, err := decodeValueDescriptor(r.Body, lc) - if err != nil { - httpErrorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.Common.JsonDecoding, - errorconcept.ValueDescriptors.Invalid, - }, - errorconcept.Default.InternalServerError) - return - } - - err = updateValueDescriptor(vd, lc, dbClient, configuration) - if err != nil { - httpErrorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.Database.NotFoundTyped, - errorconcept.ValueDescriptors.Invalid, - errorconcept.ValueDescriptors.SingleInUse, - errorconcept.ValueDescriptors.MultipleInUse, - }, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("true")) - } -} - -// Delete the value descriptor based on the ID -// DataValidationException (HTTP 409) - The value descriptor is still referenced by readings -// NotFoundException (404) - Can't find the value descriptor -// valuedescriptor/id/{id} -func deleteValueDescriptorByIdHandler(w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - id := vars["id"] - - err := deleteValueDescriptorById(id, lc, dbClient) - if err != nil { - httpErrorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.Database.NotFoundTyped, - errorconcept.ValueDescriptors.Invalid, - errorconcept.ValueDescriptors.SingleInUse, - errorconcept.ValueDescriptors.MultipleInUse, - errorconcept.Common.InvalidID, - }, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("true")) -} - -// Value descriptors based on name -// api/v1/valuedescriptor/name/{name} -func valueDescriptorByNameHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - name, err := url.QueryUnescape(vars["name"]) - - // Problems unescaping - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - switch r.Method { - case http.MethodGet: - v, err := dbClient.ValueDescriptorByName(name) - if err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.NewServiceClientHttpError(err), - errorconcept.Default.InternalServerError) - return - } - pkg.Encode(v, w, lc) - case http.MethodDelete: - if err = deleteValueDescriptorByName(name, lc, dbClient); err != nil { - httpErrorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.Database.NotFoundTyped, - errorconcept.ValueDescriptors.Invalid, - errorconcept.ValueDescriptors.SingleInUse, - errorconcept.ValueDescriptors.MultipleInUse, - }, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("true")) - } -} - -// Get a value descriptor based on the ID -// HTTP 404 not found if the ID isn't in the database -// api/v1/valuedescriptor/{id} -func valueDescriptorByIdHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - id := vars["id"] - - switch r.Method { - case http.MethodGet: - vd, err := getValueDescriptorById(id, lc, dbClient) - if err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.Database.NotFoundTyped, - errorconcept.Default.InternalServerError) - return - } - - pkg.Encode(vd, w, lc) - } -} - -// Get the value descriptor from the UOM label -// api/v1/valuedescriptor/uomlabel/{uomLabel} -func valueDescriptorByUomLabelHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - uomLabel, err := url.QueryUnescape(vars["uomLabel"]) - - // Problem unescaping - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - switch r.Method { - case http.MethodGet: - vdList, err := getValueDescriptorsByUomLabel(uomLabel, lc, dbClient) - if err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.Database.NotFoundTyped, - errorconcept.Default.InternalServerError) - return - } - - pkg.Encode(vdList, w, lc) - } -} - -// Get value descriptors who have one of the labels -// api/v1/valuedescriptor/label/{label} -func valueDescriptorByLabelHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - label, err := url.QueryUnescape(vars["label"]) - - // Problem unescaping - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - switch r.Method { - case http.MethodGet: - v, err := getValueDescriptorsByLabel(label, lc, dbClient) - if err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.Database.NotFoundTyped, - errorconcept.Default.InternalServerError) - return - } - - pkg.Encode(v, w, lc) - } -} - -// Return the value descriptors that are associated with a device -// The value descriptor is expected parameters on puts or expected values on get/put commands -// api/v1/valuedescriptor/devicename/{device} -func valueDescriptorByDeviceHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - mdc metadata.DeviceClient, - httpErrorHandler errorconcept.ErrorHandler) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - - device, err := url.QueryUnescape(vars["device"]) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - ctx := r.Context() - // Get the value descriptors - vdList, err := getValueDescriptorsByDeviceName(ctx, device, lc, dbClient, mdc) - if err != nil { - httpErrorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.Database.NotFoundTyped, - errorconcept.NewServiceClientHttpError(err), - }, - errorconcept.Default.InternalServerError) - return - } - - pkg.Encode(vdList, w, lc) -} - -// Return the value descriptors that are associated with the device specified by the device ID -// Associated value descripts are expected parameters of PUT commands and expected results of PUT/GET commands -// api/v1/valuedescriptor/deviceid/{id} -func valueDescriptorByDeviceIdHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - mdc metadata.DeviceClient, - httpErrorHandler errorconcept.ErrorHandler) { - - defer func() { _ = r.Body.Close() }() - - vars := mux.Vars(r) - - deviceId, err := url.QueryUnescape(vars["id"]) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - ctx := r.Context() - // Get the value descriptors - vdList, err := getValueDescriptorsByDeviceId(ctx, deviceId, lc, dbClient, mdc) - if err != nil { - httpErrorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.NewServiceClientHttpError(err), - errorconcept.Database.NotFoundTyped, - }, - errorconcept.Default.InternalServerError) - return - } - - pkg.Encode(vdList, w, lc) -} - -// restValueDescriptorsUsageHandler checks if value descriptors are currently being used. -// This functionality is useful for determining if a value descriptor can be updated, or deleted. -// This functionality does not provide any guarantee that the value descriptor will not be in use in the near future. -// Any functionality using the check to perform updates or deletes is responsible for handling any race conditions which -// may occur. -// Returns a map[string]bool where the key is the ValueDescriptor Name and the value is a bool stating if the -// ValueDescriptor is currently in use. -func restValueDescriptorsUsageHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - httpErrorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - qparams, err := url.ParseQuery(r.URL.RawQuery) - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - namesFilter := qparams[NAMES] - var vds []contract.ValueDescriptor - var op value_descriptor.GetValueDescriptorsExecutor - if len(namesFilter) <= 0 { - // We are not filtering so get all the value descriptors - op = value_descriptor.NewGetValueDescriptorsExecutor(dbClient, lc, configuration.Service) - } else { - op = value_descriptor.NewGetValueDescriptorsNameExecutor( - strings.Split(namesFilter[0], ","), - dbClient, - lc, - configuration.Service) - } - - vds, err = op.Execute() - if err != nil { - httpErrorHandler.HandleOneVariant( - w, - err, - errorconcept.ValueDescriptors.LimitExceeded, - errorconcept.Default.InternalServerError) - return - } - - // Use this data structure so that we can obtain the desired JSON format. Please see RAML for response format - // information. - resp := make([]map[string]bool, 0) - var ops readingOperator.GetReadingsExecutor - for _, vd := range vds { - ops = readingOperator.NewGetReadingsNameExecutor( - vd.Name, - ValueDescriptorUsageReadLimit, - dbClient, - lc, - configuration.Service) - r, err := ops.Execute() - if err != nil { - httpErrorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - if len(r) > 0 { - resp = append(resp, map[string]bool{vd.Name: true}) - continue - } - - resp = append(resp, map[string]bool{vd.Name: false}) - } - - pkg.Encode(resp, w, lc) -} diff --git a/internal/core/data/router_test.go b/internal/core/data/router_test.go deleted file mode 100644 index a7f69ba351..0000000000 --- a/internal/core/data/router_test.go +++ /dev/null @@ -1,288 +0,0 @@ -/* - * ****************************************************************************** - * Copyright 2019 Dell Inc. - * - * 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 data - -import ( - "encoding/json" - "errors" - "io/ioutil" - "net/http" - "net/http/httptest" - "reflect" - "strconv" - "strings" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/core/data/config" - "github.com/edgexfoundry/edgex-go/internal/core/data/interfaces" - "github.com/edgexfoundry/edgex-go/internal/core/data/interfaces/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -// TestSuccessfulConfig configuration used to avoid MaxResultCount errors. -var TestSuccessfulConfig = bootstrapConfig.ServiceInfo{MaxResultCount: 5} - -// ErrorQueryParam query parameter value which will trigger the ' url.ParseQuery' function to throw an error due to the '%' not being followed by a valid hexadecimal number. -var ErrorQueryParam = "%zz" - -var TestURI = "/valuedescriptor" - -// TestError error used to simulate non EdgeX type errors. -var TestError = errors.New("test error") - -// TestNonExistentValueDescriptorName name used when attempting to simulate no matching results. -var TestNonExistentValueDescriptorName = "Non-existent ValueDescriptor" - -// TestErrorValueDescriptorName name used when attempting to simulate an error. -var TestErrorValueDescriptorName = "TestErrorValueDescriptor" - -var TestValueDescriptor1 = contract.ValueDescriptor{ - Name: "TestValueDescriptor1", - Description: "Test Value descriptor associated with no readings", -} -var TestValueDescriptor2 = contract.ValueDescriptor{ - Name: "TestValueDescriptor2", - Description: "Test Value descriptor which associated with 1 reading", -} -var TestValueDescriptor3 = contract.ValueDescriptor{ - Name: "TestValueDescriptor3", - Description: "Test Value descriptor which associated with 2 reading", -} - -func TestRestValueDescriptorsUsageHandler(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - config bootstrapConfig.ServiceInfo - expectedResult map[string]bool - expectedStatus int - }{ - { - "OK response with no query parameter", - createRequest(), - createMockLoader(), - TestSuccessfulConfig, - map[string]bool{ - TestValueDescriptor1.Name: false, - TestValueDescriptor2.Name: true, - TestValueDescriptor3.Name: true, - }, - http.StatusOK, - }, - { - "OK response one name with as name query parameter", - createRequestWithQueryParameter(map[string][]string{NAMES: {TestValueDescriptor1.Name}}), - createMockLoader(), - TestSuccessfulConfig, - map[string]bool{ - TestValueDescriptor1.Name: false, - }, - http.StatusOK, - }, - { - "OK response with multiple names as query parameter", - createRequestWithQueryParameter(map[string][]string{NAMES: {TestValueDescriptor1.Name, TestValueDescriptor2.Name, TestValueDescriptor3.Name}}), - createMockLoader(), - TestSuccessfulConfig, - map[string]bool{ - TestValueDescriptor1.Name: false, - TestValueDescriptor2.Name: true, - TestValueDescriptor3.Name: true, - }, - http.StatusOK, - }, - { - "OK - No matching value descriptors without query parameter", - createRequest(), - createMockLoaderNoValueDescriptors(), - TestSuccessfulConfig, - map[string]bool{}, - http.StatusOK, - }, - { - "OK - No matching value descriptors with one name as query parameter", - createRequestWithQueryParameter(map[string][]string{NAMES: {TestNonExistentValueDescriptorName}}), - createMockLoader(), - TestSuccessfulConfig, - map[string]bool{}, - http.StatusOK, - }, - { - "OK - No matching value descriptors with multiple names as query parameter", - createRequestWithQueryParameter(map[string][]string{NAMES: {TestNonExistentValueDescriptorName, TestNonExistentValueDescriptorName}}), - createMockLoader(), - TestSuccessfulConfig, - map[string]bool{}, - http.StatusOK, - }, - { - "Invalid name query parameter", - createRequestWithInvalidParameter(map[string][]string{}), - createMockLoader(), - TestSuccessfulConfig, - nil, - http.StatusBadRequest, - }, - { - "Invalid extra query parameter", - createRequestWithInvalidParameter(map[string][]string{NAMES: {TestNonExistentValueDescriptorName, TestNonExistentValueDescriptorName}}), - createMockLoader(), - TestSuccessfulConfig, - nil, - http.StatusBadRequest, - }, { - "GetValueDescriptorsExecutor Error", - createRequest(), - createMockLoaderValueDescriptorsExecutorError(), - TestSuccessfulConfig, - nil, - http.StatusInternalServerError, - }, - { - "GetReadingsExecutor Error", - createRequest(), - createMockLoaderReadingsExecutorError(), - TestSuccessfulConfig, - nil, - http.StatusInternalServerError, - }, - { - "Error Limit Exceeded", - createRequestWithQueryParameter(map[string][]string{NAMES: {TestValueDescriptor1.Name, TestValueDescriptor2.Name, TestValueDescriptor3.Name}}), - createMockLoader(), - bootstrapConfig.ServiceInfo{MaxResultCount: 1}, - nil, - http.StatusRequestEntityTooLarge, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - configuration := &config.ConfigurationStruct{Service: bootstrapConfig.ServiceInfo{MaxResultCount: 10}} - configuration.Service = tt.config - rr := httptest.NewRecorder() - lc := logger.NewMockClient() - restValueDescriptorsUsageHandler(rr, tt.request, lc, tt.dbMock, errorconcept.NewErrorHandler(lc), configuration) - response := rr.Result() - b, _ := ioutil.ReadAll(response.Body) - var r []map[string]bool - _ = json.Unmarshal(b, &r) - observed := convertResponseToMap(r) - if tt.expectedResult != nil && !reflect.DeepEqual(tt.expectedResult, observed) { - t.Errorf("Observed result doesn't match expected.\nExpected: %v\nActual: %v\n", tt.expectedResult, observed) - } - - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func createRequest() *http.Request { - return httptest.NewRequest(http.MethodGet, TestURI, nil) -} - -func createRequestWithQueryParameter(queryParam map[string][]string) *http.Request { - req := httptest.NewRequest(http.MethodGet, TestURI, nil) - q := req.URL.Query() - for k, v := range queryParam { - q.Add(k, strings.Join(v, ",")) - } - req.URL.RawQuery = q.Encode() - return req -} - -func createRequestWithInvalidParameter(queryParam map[string][]string) *http.Request { - req := createRequestWithQueryParameter(queryParam) - req.URL.RawQuery = req.URL.RawQuery + ErrorQueryParam - return req -} - -func createReadings(howMany int) []contract.Reading { - var readings []contract.Reading - for i := 0; i < howMany; i++ { - readings = append(readings, contract.Reading{ - Id: "reading" + strconv.Itoa(i), - }) - } - return readings -} - -func createMockLoader() interfaces.DBClient { - dbMock := mocks.DBClient{} - dbMock.On("ValueDescriptors").Return([]contract.ValueDescriptor{TestValueDescriptor1, TestValueDescriptor2, TestValueDescriptor3}, nil) - dbMock.On("ValueDescriptorsByName", []string{TestValueDescriptor1.Name}).Return([]contract.ValueDescriptor{TestValueDescriptor1}, nil) - dbMock.On("ValueDescriptorsByName", []string{TestValueDescriptor1.Name, TestValueDescriptor2.Name}).Return([]contract.ValueDescriptor{TestValueDescriptor1, TestValueDescriptor2}, nil) - dbMock.On("ValueDescriptorsByName", []string{TestValueDescriptor1.Name, TestValueDescriptor2.Name, TestValueDescriptor3.Name}).Return([]contract.ValueDescriptor{TestValueDescriptor1, TestValueDescriptor2, TestValueDescriptor3}, nil) - dbMock.On("ValueDescriptorsByName", []string{TestNonExistentValueDescriptorName}).Return(nil, nil) - dbMock.On("ValueDescriptorsByName", []string{TestNonExistentValueDescriptorName, TestNonExistentValueDescriptorName}).Return(nil, nil) - dbMock.On("ReadingsByValueDescriptor", TestValueDescriptor1.Name, ValueDescriptorUsageReadLimit).Return(createReadings(0), nil) - dbMock.On("ReadingsByValueDescriptor", TestValueDescriptor2.Name, ValueDescriptorUsageReadLimit).Return(createReadings(1), nil) - dbMock.On("ReadingsByValueDescriptor", TestValueDescriptor3.Name, ValueDescriptorUsageReadLimit).Return(createReadings(2), nil) - - return &dbMock -} - -func createMockLoaderNoValueDescriptors() interfaces.DBClient { - dbMock := mocks.DBClient{} - dbMock.On("ValueDescriptors").Return([]contract.ValueDescriptor{}, nil) - - return &dbMock -} - -func createMockLoaderValueDescriptorsExecutorError() interfaces.DBClient { - dbMock := mocks.DBClient{} - dbMock.On("ValueDescriptors").Return(nil, TestError) - dbMock.On("ValueDescriptorsByName", []string{TestErrorValueDescriptorName}).Return(nil, TestError) - dbMock.On("ValueDescriptorsByName", []string{TestErrorValueDescriptorName}).Return(nil, TestError) - - return &dbMock -} -func createMockLoaderReadingsExecutorError() interfaces.DBClient { - dbMock := mocks.DBClient{} - dbMock.On("ValueDescriptors").Return([]contract.ValueDescriptor{TestValueDescriptor1, TestValueDescriptor2, TestValueDescriptor3}, nil) - dbMock.On("ValueDescriptorsByName", []string{TestValueDescriptor1.Name}).Return([]contract.ValueDescriptor{TestValueDescriptor1}, nil) - dbMock.On("ValueDescriptorsByName", []string{TestValueDescriptor1.Name, TestValueDescriptor2.Name}).Return([]contract.ValueDescriptor{TestValueDescriptor1, TestValueDescriptor2}, nil) - dbMock.On("ValueDescriptorsByName", []string{TestValueDescriptor1.Name, TestValueDescriptor2.Name, TestValueDescriptor3.Name}).Return([]contract.ValueDescriptor{TestValueDescriptor1, TestValueDescriptor2, TestValueDescriptor3}, nil) - dbMock.On("ValueDescriptorsByName", []string{TestNonExistentValueDescriptorName}).Return(nil, nil) - dbMock.On("ValueDescriptorsByName", []string{TestNonExistentValueDescriptorName, TestNonExistentValueDescriptorName}).Return(nil, nil) - dbMock.On("ReadingsByValueDescriptor", TestValueDescriptor1.Name, ValueDescriptorUsageReadLimit).Return(nil, TestError) - dbMock.On("ReadingsByValueDescriptor", TestValueDescriptor2.Name, ValueDescriptorUsageReadLimit).Return(nil, TestError) - dbMock.On("ReadingsByValueDescriptor", TestValueDescriptor3.Name, ValueDescriptorUsageReadLimit).Return(nil, TestError) - - return &dbMock -} - -// convertResponseToMap converts the response from the restValueDescriptorsUsageHandler to a map where the key is the -// ValueDescriptor name and the value is a bool specifying the usage state. This is made to make testing easier and -// more readable. -func convertResponseToMap(resp []map[string]bool) map[string]bool { - convertedMap := map[string]bool{} - for _, m := range resp { - for k, v := range m { - convertedMap[k] = v - } - } - - return convertedMap -} diff --git a/internal/core/data/utils.go b/internal/core/data/utils.go deleted file mode 100644 index f16279d375..0000000000 --- a/internal/core/data/utils.go +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * - * 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 data - -import ( - "io" - "io/ioutil" - - "github.com/edgexfoundry/edgex-go/internal/core/data/config" - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" -) - -// Printing function purely for debugging purposes -// Print the body of a request to the console -func printBody(r io.ReadCloser, lc logger.LoggingClient) { - body, err := ioutil.ReadAll(r) - bodyString := string(body) - - if err != nil { - lc.Error(err.Error()) - } - - lc.Info(bodyString) -} - -func checkMaxLimit(limit int, lc logger.LoggingClient, configuration *config.ConfigurationStruct) error { - if limit > configuration.Service.MaxResultCount { - lc.Error(maxExceededString) - return errors.NewErrLimitExceeded(limit) - } - - return nil -} diff --git a/internal/core/data/validate.go b/internal/core/data/validate.go deleted file mode 100644 index 2c15398feb..0000000000 --- a/internal/core/data/validate.go +++ /dev/null @@ -1,119 +0,0 @@ -// -// Copyright (c) 2018 -// Cavium -// -// SPDX-License-Identifier: Apache-2.0 -// - -package data - -import ( - "encoding/json" - "fmt" - "strconv" - - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -func isValidValueDescriptor(vd models.ValueDescriptor, reading models.Reading) error { - var err error - switch vd.Type { - case "B": // boolean - err = validBoolean(reading) - case "F": // floating point - err = validFloat(reading, vd) - case "I": // integer - err = validInteger(reading, vd) - case "S": // string or character data - err = validString(reading) - case "J": // JSON data - err = validJSON(reading) - default: - err = fmt.Errorf("Unknown type") - } - if err != nil { - return errors.NewErrValueDescriptorInvalid(vd.Name, err) - } - return nil -} - -func validBoolean(reading models.Reading) error { - _, err := strconv.ParseBool(reading.Value) - return err -} - -func validFloat(reading models.Reading, vd models.ValueDescriptor) error { - value, err := strconv.ParseFloat(reading.Value, 64) - if err != nil { - return err - } - - if (vd.Max != nil) && (vd.Max != "") { - max, err := strconv.ParseFloat(vd.Max.(string), 64) - if err != nil { - return err - } - - if value > max { - return fmt.Errorf("Value is over the limits") - } - } - - if (vd.Min != nil) && (vd.Min != "") { - min, err := strconv.ParseFloat(vd.Min.(string), 64) - if err != nil { - return err - } - - if value < min { - return fmt.Errorf("Value is under the limits") - } - } - - return nil -} - -func validInteger(reading models.Reading, vd models.ValueDescriptor) error { - value, err := strconv.ParseInt(reading.Value, 10, 64) - if err != nil { - return err - } - - if (vd.Max != nil) && (vd.Max != "") { - max, err := strconv.ParseInt(vd.Max.(string), 10, 64) - if err != nil { - return err - } - - if value > max { - return fmt.Errorf("Value is over the limits") - } - } - - if (vd.Min != nil) && (vd.Min != "") { - min, err := strconv.ParseInt(vd.Min.(string), 10, 64) - if err != nil { - return err - } - - if value < min { - return fmt.Errorf("Value is under the limits") - } - } - - return nil -} - -func validString(reading models.Reading) error { - if reading.Value == "" { - return fmt.Errorf("Value is empty") - } - - return nil -} - -func validJSON(reading models.Reading) error { - var js interface{} - return json.Unmarshal([]byte(reading.Value), &js) -} diff --git a/internal/core/data/validate_test.go b/internal/core/data/validate_test.go deleted file mode 100644 index 0c9bdc9cf8..0000000000 --- a/internal/core/data/validate_test.go +++ /dev/null @@ -1,258 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Inc. - * - * 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 data - -import ( - "testing" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -func TestValidBoolean(t *testing.T) { - var tests = []struct { - name string - value string - err bool - }{ - {"false, nil", "false", false}, - {"true, nil", "true", false}, - {"True", "True", false}, - {"TRUE", "TRUE", false}, - {"false", "false", false}, - - {"dummy, not nil ", "dummy", true}, - {"void", "", true}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var reading = models.Reading{Value: tt.value} - err := validBoolean(reading) - if err == nil { - if tt.err { - t.Errorf("There should be an error: %v", err) - } - } else { - if !tt.err { - t.Errorf("There should not be an error: %v", err) - } - } - }) - - } -} - -func TestValidFloat(t *testing.T) { - - var tests = []struct { - name string - value string - min string - max string - err bool - }{ - {"value", "-10.10", "-20", "20", false}, - - {"novalue", "", "-20", "20", true}, - {"not_float", "data", "-20", "20", true}, - - {"minmaxEmpty", "-10.10", "", "", false}, - - {"min_lower", "-30", "-20", "20", true}, - {"max_higher", "4000", "-20", "20", true}, - - {"notvalidmin", "-10.10", "true", "20", true}, - {"notvalidmax", "-10.10", "", "data", true}, - - {"notvalidminmax", "-10.10", "true", "false", true}, - - {"onlymin", "-10.10", "-20", "", false}, - {"onlymax", "-10.10", "", "20", false}, - - {"onlymin_lower", "-110.1", "-20", "", true}, - {"onlymax_higher", "110.2", "", "20.09", true}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var reading = models.Reading{Value: tt.value} - tvd := models.ValueDescriptor{Min: tt.min, Max: tt.max} - err := validFloat(reading, tvd) - if err == nil { - if tt.err { - t.Errorf("There should be an error: %v", err) - } - } else { - if !tt.err { - t.Errorf("There should not be an error: %v", err) - } - } - }) - - } -} - -func TestValidInteger(t *testing.T) { - - var tests = []struct { - name string - value string - min string - max string - err bool - }{ - {"value", "-10", "-20", "20", false}, - {"novalue", "", "", "", true}, - {"no_integer", "data", "-20", "20", true}, - - {"minmaxEmpty", "-10", "", "", false}, - - {"min_lower", "-30", "-20", "20", true}, - {"max_higher", "4000", "-20", "20", true}, - - {"onlymin", "-10", "-20", "", false}, - {"onlymax", "-10", "", "20", false}, - - {"notvalidmin", "-10", "true", "20", true}, - {"notvalidmax", "-10", "", "data", true}, - - {"notvalidminmax", "-10", "true", "false", true}, - - {"onlymin_lower", "-110", "-20", "", true}, - {"onlymax_higher", "110", "", "20", true}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var reading = models.Reading{Value: tt.value} - tvd := models.ValueDescriptor{Min: tt.min, Max: tt.max} - err := validInteger(reading, tvd) - if err == nil { - if tt.err { - t.Errorf("There should be an error: %v", err) - } - } else { - if !tt.err { - t.Errorf("There should not be an error: %v", err) - } - } - }) - - } -} - -func TestValidString(t *testing.T) { - - var tests = []struct { - name string - value string - err bool - }{ - {"empty", "", true}, - {"valid", "test string", false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var reading = models.Reading{Value: tt.value} - err := validString(reading) - if err == nil { - if tt.err { - t.Errorf("There should be an error: %v", err) - } - } else { - if !tt.err { - t.Errorf("There should not be an error: %v", err) - } - } - }) - - } -} - -func TestValidJson(t *testing.T) { - - var tests = []struct { - name string - value string - err bool - }{ - {"empty", "", true}, - {"valid", "{\"test\": \"string\"}", false}, - {"novalid", "test string", true}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var reading = models.Reading{Value: tt.value} - err := validJSON(reading) - if err == nil { - if tt.err { - t.Errorf("There should be an error: %v", err) - } - } else { - if !tt.err { - t.Errorf("There should not be an error: %v", err) - } - } - }) - - } -} - -func TestIsValidValueDescriptor_private(t *testing.T) { - - var tests = []struct { - tvd string - value string - err bool - }{ - {"", "", true}, - {"B", "true", false}, - {"b", "", true}, - {"P", "", true}, - - {"F", "-10.5", false}, - {"f", "", true}, - {"P", "", true}, - - {"I", "-23", false}, - {"i", "", true}, - {"P", "", true}, - - {"S", "test string", false}, - {"s", "", true}, - {"P", "", true}, - - {"J", "{\"test\": \"string\"}", false}, - {"j", "", true}, - {"P", "", true}, - } - - for _, tt := range tests { - t.Run(tt.value, func(t *testing.T) { - tvd := models.ValueDescriptor{Type: tt.tvd} - var reading = models.Reading{Value: tt.value} - err := isValidValueDescriptor(tvd, reading) - if err == nil { - if tt.err { - t.Errorf("There should be an error: %v", err) - } - } else { - if !tt.err { - t.Errorf("There should not be an error: %v", err) - } - } - }) - } -} diff --git a/internal/core/data/valuedescriptor.go b/internal/core/data/valuedescriptor.go deleted file mode 100644 index d005c7de02..0000000000 --- a/internal/core/data/valuedescriptor.go +++ /dev/null @@ -1,409 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * - * 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 data - -import ( - "context" - "encoding/json" - "fmt" - "io" - "regexp" - - "github.com/edgexfoundry/edgex-go/internal/core/data/config" - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - "github.com/edgexfoundry/edgex-go/internal/core/data/interfaces" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/metadata" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -const ( - formatSpecifier = "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])" - maxExceededString string = "Error, exceeded the max limit as defined in config" -) - -// Check if the value descriptor matches the format string regular expression -func validateFormatString(v contract.ValueDescriptor, lc logger.LoggingClient) error { - // No formatting specified - if v.Formatting == "" { - return nil - } - - match, err := regexp.MatchString(formatSpecifier, v.Formatting) - - if err != nil { - lc.Error("Error checking for format string for value descriptor " + v.Name) - return err - } - if !match { - err = fmt.Errorf("format is not a valid printf format") - lc.Error(fmt.Sprintf("Error posting value descriptor. %s", err.Error())) - return errors.NewErrValueDescriptorInvalid(v.Name, err) - } - - return nil -} - -func getValueDescriptorByName( - name string, - lc logger.LoggingClient, - dbClient interfaces.DBClient) (vd contract.ValueDescriptor, err error) { - - vd, err = dbClient.ValueDescriptorByName(name) - - if err != nil { - lc.Error(err.Error()) - if err == db.ErrNotFound { - return contract.ValueDescriptor{}, errors.NewErrDbNotFound() - } else { - return contract.ValueDescriptor{}, err - } - } - - return vd, nil -} - -func getValueDescriptorById( - id string, - lc logger.LoggingClient, - dbClient interfaces.DBClient) (vd contract.ValueDescriptor, err error) { - - vd, err = dbClient.ValueDescriptorById(id) - - if err != nil { - lc.Error(err.Error()) - if err == db.ErrNotFound { - return contract.ValueDescriptor{}, errors.NewErrDbNotFound() - } else if err == db.ErrInvalidObjectId { - return contract.ValueDescriptor{}, errors.NewErrInvalidId(id) - } else { - return contract.ValueDescriptor{}, err - } - } - - return vd, nil -} - -func getValueDescriptorsByUomLabel( - uomLabel string, - lc logger.LoggingClient, - dbClient interfaces.DBClient) (vdList []contract.ValueDescriptor, err error) { - - vdList, err = dbClient.ValueDescriptorsByUomLabel(uomLabel) - - if err != nil { - lc.Error(err.Error()) - if err == db.ErrNotFound { - return []contract.ValueDescriptor{}, errors.NewErrDbNotFound() - } else { - return []contract.ValueDescriptor{}, err - } - } - - return vdList, nil -} - -func getValueDescriptorsByLabel( - label string, - lc logger.LoggingClient, - dbClient interfaces.DBClient) (vdList []contract.ValueDescriptor, err error) { - - vdList, err = dbClient.ValueDescriptorsByLabel(label) - - if err != nil { - lc.Error(err.Error()) - if err == db.ErrNotFound { - return []contract.ValueDescriptor{}, errors.NewErrDbNotFound() - } else { - return []contract.ValueDescriptor{}, err - } - } - - return vdList, nil -} - -func getValueDescriptorsByType( - typ string, - lc logger.LoggingClient, - dbClient interfaces.DBClient) (vdList []contract.ValueDescriptor, err error) { - - vdList, err = dbClient.ValueDescriptorsByType(typ) - - if err != nil { - lc.Error(err.Error()) - if err == db.ErrNotFound { - return []contract.ValueDescriptor{}, errors.NewErrDbNotFound() - } else { - return []contract.ValueDescriptor{}, err - } - } - - return vdList, nil -} - -func getValueDescriptorsByDevice( - device contract.Device, - lc logger.LoggingClient, - dbClient interfaces.DBClient) (vdList []contract.ValueDescriptor, err error) { - - // Get the names of the value descriptors - vdNames := []string{} - device.AllAssociatedValueDescriptors(&vdNames) - - // Get the value descriptors - vdList = []contract.ValueDescriptor{} - for _, name := range vdNames { - vd, err := getValueDescriptorByName(name, lc, dbClient) - - // Not an error if not found - if err != nil { - switch err.(type) { - case errors.ErrDbNotFound: - continue - default: - return []contract.ValueDescriptor{}, err - } - } - - vdList = append(vdList, vd) - } - - return vdList, nil -} - -func getValueDescriptorsByDeviceName( - ctx context.Context, - name string, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - mdc metadata.DeviceClient) (vdList []contract.ValueDescriptor, err error) { - - // Get the device - device, err := mdc.DeviceForName(ctx, name) - if err != nil { - lc.Error("Problem getting device from metadata: " + err.Error()) - return []contract.ValueDescriptor{}, err - } - - return getValueDescriptorsByDevice(device, lc, dbClient) -} - -func getValueDescriptorsByDeviceId( - ctx context.Context, - id string, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - mdc metadata.DeviceClient) (vdList []contract.ValueDescriptor, err error) { - - // Get the device - device, err := mdc.Device(ctx, id) - if err != nil { - lc.Error("Problem getting device from metadata: " + err.Error()) - return []contract.ValueDescriptor{}, err - } - - return getValueDescriptorsByDevice(device, lc, dbClient) -} - -func getAllValueDescriptors( - lc logger.LoggingClient, - dbClient interfaces.DBClient) (vd []contract.ValueDescriptor, err error) { - - vd, err = dbClient.ValueDescriptors() - if err != nil { - lc.Error(err.Error()) - return nil, err - } - - return vd, nil -} - -func decodeValueDescriptor( - reader io.ReadCloser, - lc logger.LoggingClient) (vd contract.ValueDescriptor, err error) { - - v := contract.ValueDescriptor{} - err = json.NewDecoder(reader).Decode(&v) - // Problems decoding - if err != nil { - lc.Error("Error decoding the value descriptor: " + err.Error()) - return contract.ValueDescriptor{}, errors.NewErrJsonDecoding(v.Name) - } - - // Check the formatting - err = validateFormatString(v, lc) - if err != nil { - return contract.ValueDescriptor{}, err - } - - return v, nil -} - -func addValueDescriptor( - vd contract.ValueDescriptor, - lc logger.LoggingClient, - dbClient interfaces.DBClient) (id string, err error) { - - id, err = dbClient.AddValueDescriptor(vd) - if err != nil { - lc.Error(err.Error()) - if err == db.ErrNotUnique { - return "", errors.NewErrDuplicateValueDescriptorName(vd.Name) - } else { - return "", err - } - } - - return id, nil -} - -func updateValueDescriptor( - from contract.ValueDescriptor, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - configuration *config.ConfigurationStruct) error { - - to, err := getValueDescriptorById(from.Id, lc, dbClient) - if err != nil { - return err - } - - // Update the fields - if from.Description != "" { - to.Description = from.Description - } - if from.DefaultValue != "" { - to.DefaultValue = from.DefaultValue - } - if from.Formatting != "" { - match, err := regexp.MatchString(formatSpecifier, from.Formatting) - if err != nil { - lc.Error("Error checking formatting for updated value descriptor") - return err - } - if !match { - lc.Error("value descriptor's format string doesn't fit the required pattern: " + formatSpecifier) - return errors.NewErrValueDescriptorInvalid(from.Name, err) - } - to.Formatting = from.Formatting - } - if from.Labels != nil { - to.Labels = from.Labels - } - - if from.Max != "" { - to.Max = from.Max - } - if from.Min != "" { - to.Min = from.Min - } - if from.Name != "" { - // Check if value descriptor is still in use by readings if the name changes - if from.Name != to.Name { - // Arbitrary limit, we're just checking if there are any readings - r, err := getReadingsByValueDescriptor(to.Name, 10, lc, dbClient, configuration) - if err != nil { - lc.Error("Error checking the readings for the value descriptor: " + err.Error()) - return err - } - // Value descriptor is still in use - if len(r) != 0 { - lc.Error( - "Data integrity issue. Value Descriptor with name: " + - from.Name + - " is still referenced by existing readings.") - return errors.NewErrValueDescriptorInUse(from.Name) - } - } - to.Name = from.Name - } - if from.Origin != 0 { - to.Origin = from.Origin - } - if from.Type != "" { - to.Type = from.Type - } - if from.UomLabel != "" { - to.UomLabel = from.UomLabel - } - - // Push the updated valuedescriptor to the database - err = dbClient.UpdateValueDescriptor(to) - if err != nil { - if err == db.ErrNotUnique { - lc.Error("Value descriptor name is not unique") - return errors.NewErrDuplicateValueDescriptorName(to.Name) - } else { - lc.Error(err.Error()) - return err - } - } - - return nil -} - -func deleteValueDescriptor( - vd contract.ValueDescriptor, - lc logger.LoggingClient, - dbClient interfaces.DBClient) error { - - // Check if the value descriptor is still in use by readings - readings, err := dbClient.ReadingsByValueDescriptor(vd.Name, 10) - if err != nil { - lc.Error(err.Error()) - return err - } - if len(readings) > 0 { - lc.Error("Data integrity issue. Value Descriptor is still referenced by existing readings.") - return errors.NewErrValueDescriptorInUse(vd.Name) - } - - // Delete the value descriptor - if err = dbClient.DeleteValueDescriptorById(vd.Id); err != nil { - lc.Error(err.Error()) - return err - } - - return nil -} - -func deleteValueDescriptorByName(name string, lc logger.LoggingClient, dbClient interfaces.DBClient) error { - // Check if the value descriptor exists - vd, err := getValueDescriptorByName(name, lc, dbClient) - if err != nil { - return err - } - - if err = deleteValueDescriptor(vd, lc, dbClient); err != nil { - return err - } - - return nil -} - -func deleteValueDescriptorById(id string, lc logger.LoggingClient, dbClient interfaces.DBClient) error { - // Check if the value descriptor exists - vd, err := getValueDescriptorById(id, lc, dbClient) - if err != nil { - return err - } - - if err = deleteValueDescriptor(vd, lc, dbClient); err != nil { - return err - } - - return nil -} diff --git a/internal/core/data/valuedescriptor_test.go b/internal/core/data/valuedescriptor_test.go deleted file mode 100644 index 96084913a2..0000000000 --- a/internal/core/data/valuedescriptor_test.go +++ /dev/null @@ -1,449 +0,0 @@ -package data - -import ( - "bytes" - "context" - "fmt" - "net/http" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - "github.com/edgexfoundry/edgex-go/internal/core/data/interfaces/mocks" - dataMocks "github.com/edgexfoundry/edgex-go/internal/core/data/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/types" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/stretchr/testify/mock" -) - -func TestValidateFormatString(t *testing.T) { - err := validateFormatString(models.ValueDescriptor{Formatting: "%s"}, logger.NewMockClient()) - - if err != nil { - t.Errorf("Should match format specifier") - } -} - -func TestValidateFormatStringEmpty(t *testing.T) { - err := validateFormatString(models.ValueDescriptor{Formatting: ""}, logger.NewMockClient()) - - if err != nil { - t.Errorf("Should match format specifier") - } -} - -func TestValidateFormatStringInvalid(t *testing.T) { - err := validateFormatString(models.ValueDescriptor{Formatting: "error"}, logger.NewMockClient()) - - if err == nil { - t.Errorf("Expected error on invalid format string") - } -} - -func TestGetValueDescriptorByName(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ValueDescriptorByName", mock.Anything).Return(models.ValueDescriptor{Id: testUUIDString}, nil) - valueDescriptor, err := getValueDescriptorByName("valid", logger.NewMockClient(), dbClientMock) - if err != nil { - t.Errorf("Unexpected error getting value descriptor by name") - } - - if valueDescriptor.Id != testUUIDString { - t.Errorf("ID returned doesn't match db") - } -} - -func TestGetValueDescriptorByNameNotFound(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ValueDescriptorByName", mock.Anything).Return(models.ValueDescriptor{}, db.ErrNotFound) - _, err := getValueDescriptorByName("404", logger.NewMockClient(), dbClientMock) - if err != nil { - switch err.(type) { - case errors.ErrDbNotFound: - return - default: - t.Errorf("Unexpected error getting value descriptor by name missing in DB") - } - } - - if err == nil { - t.Errorf("Expected error getting value descriptor by name missing in DB") - } -} - -func TestGetValueDescriptorByNameError(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ValueDescriptorByName", mock.Anything).Return(models.ValueDescriptor{}, fmt.Errorf("some error")) - _, err := getValueDescriptorByName("error", logger.NewMockClient(), dbClientMock) - if err == nil { - t.Errorf("Expected error getting value descriptor by name with some error") - } -} - -func TestGetValueDescriptorById(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ValueDescriptorById", mock.Anything).Return(models.ValueDescriptor{Id: testUUIDString}, nil) - valueDescriptor, err := getValueDescriptorById("valid", logger.NewMockClient(), dbClientMock) - if err != nil { - t.Errorf("Unexpected error getting value descriptor by ID") - } - - if valueDescriptor.Id != testUUIDString { - t.Errorf("ID returned doesn't match db") - } -} - -func TestGetValueDescriptorByIdNotFound(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ValueDescriptorById", mock.Anything).Return(models.ValueDescriptor{}, db.ErrNotFound) - _, err := getValueDescriptorById("404", logger.NewMockClient(), dbClientMock) - if err != nil { - switch err.(type) { - case errors.ErrDbNotFound: - return - default: - t.Errorf("Unexpected error getting value descriptor by ID missing in DB") - } - } - - if err == nil { - t.Errorf("Expected error getting value descriptor by ID missing in DB") - } -} - -func TestGetValueDescriptorByIdError(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ValueDescriptorById", mock.Anything).Return(models.ValueDescriptor{}, fmt.Errorf("some error")) - _, err := getValueDescriptorById("error", logger.NewMockClient(), dbClientMock) - if err == nil { - t.Errorf("Expected error getting value descriptor by ID with some error") - } -} - -func TestGetValueDescriptorsByUomLabel(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ValueDescriptorsByUomLabel", mock.Anything).Return([]models.ValueDescriptor{}, nil) - _, err := getValueDescriptorsByUomLabel("valid", logger.NewMockClient(), dbClientMock) - if err != nil { - t.Errorf("Unexpected error getting value descriptor by UOM label") - } -} - -func TestGetValueDescriptorsByUomLabelNotFound(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ValueDescriptorsByUomLabel", mock.Anything).Return([]models.ValueDescriptor{}, db.ErrNotFound) - _, err := getValueDescriptorsByUomLabel("404", logger.NewMockClient(), dbClientMock) - if err != nil { - switch err.(type) { - case errors.ErrDbNotFound: - return - default: - t.Errorf("Unexpected error getting value descriptor by UOM label missing in DB") - } - } - - if err == nil { - t.Errorf("Expected error getting value descriptor by UOM label missing in DB") - } -} - -func TestGetValueDescriptorsByUomLabelError(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ValueDescriptorsByUomLabel", mock.Anything).Return([]models.ValueDescriptor{}, fmt.Errorf("some error")) - _, err := getValueDescriptorsByUomLabel("error", logger.NewMockClient(), dbClientMock) - if err == nil { - t.Errorf("Expected error getting value descriptor by UOM label with some error") - } -} - -func TestGetValueDescriptorsByLabel(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ValueDescriptorsByLabel", mock.MatchedBy(func(name string) bool { - return name == testUUIDString - })).Return([]models.ValueDescriptor{{Id: testUUIDString}}, nil) - valueDescriptor, err := getValueDescriptorsByLabel(testUUIDString, logger.NewMockClient(), dbClientMock) - if err != nil { - t.Errorf("Unexpected error getting value descriptor by label") - } - - if valueDescriptor[0].Id != testUUIDString { - t.Errorf("ValueDescriptor received doesn't match expectation") - } -} - -func TestGetValueDescriptorsByLabelNotFound(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ValueDescriptorsByLabel", mock.Anything).Return([]models.ValueDescriptor{}, db.ErrNotFound) - _, err := getValueDescriptorsByLabel("404", logger.NewMockClient(), dbClientMock) - if err != nil { - switch err.(type) { - case errors.ErrDbNotFound: - return - default: - t.Errorf("Unexpected error getting value descriptor by label missing in DB") - } - } - - if err == nil { - t.Errorf("Expected error getting value descriptor by label missing in DB") - } -} - -func TestGetValueDescriptorsByLabelError(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ValueDescriptorsByLabel", mock.Anything).Return([]models.ValueDescriptor{}, fmt.Errorf("some error")) - _, err := getValueDescriptorsByLabel("error", logger.NewMockClient(), dbClientMock) - if err == nil { - t.Errorf("Expected error getting value descriptor by label with some error") - } -} - -func TestGetValueDescriptorsByType(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ValueDescriptorsByType", mock.Anything).Return([]models.ValueDescriptor{}, nil) - _, err := getValueDescriptorsByType("valid", logger.NewMockClient(), dbClientMock) - if err != nil { - t.Errorf("Unexpected error getting value descriptor by type") - } -} - -func TestGetValueDescriptorsByTypeNotFound(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ValueDescriptorsByType", mock.Anything).Return([]models.ValueDescriptor{}, db.ErrNotFound) - _, err := getValueDescriptorsByType("404", logger.NewMockClient(), dbClientMock) - if err != nil { - switch err.(type) { - case errors.ErrDbNotFound: - return - default: - t.Errorf("Unexpected error getting value descriptor by type missing in DB") - } - } - - if err == nil { - t.Errorf("Expected error getting value descriptor by type missing in DB") - } -} - -func TestGetValueDescriptorsByTypeError(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ValueDescriptorsByType", mock.Anything).Return([]models.ValueDescriptor{}, fmt.Errorf("some error")) - _, err := getValueDescriptorsByType("R", logger.NewMockClient(), dbClientMock) - - if err == nil { - t.Errorf("Expected error getting value descriptor by type with some error") - } -} - -func TestGetValueDescriptorsByDeviceName(t *testing.T) { - reset() - _, err := getValueDescriptorsByDeviceName(context.Background(), testDeviceName, logger.NewMockClient(), nil, dataMocks.NewMockDeviceClient()) - if err != nil { - t.Errorf("Unexpected error getting value descriptor by device name") - } -} - -func TestGetValueDescriptorsByDeviceNameNotFound(t *testing.T) { - reset() - _, err := getValueDescriptorsByDeviceName(context.Background(), "404", logger.NewMockClient(), nil, dataMocks.NewMockDeviceClient()) - if err != nil { - switch err := err.(type) { - case types.ErrServiceClient: - if err.StatusCode != http.StatusNotFound { - t.Errorf("Expected a 404 error") - } - return - default: - t.Errorf("Unexpected error getting value descriptor by device name missing in DB") - } - } - - if err == nil { - t.Errorf("Expected error getting value descriptor by device name missing in DB") - } -} - -func TestGetValueDescriptorsByDeviceNameError(t *testing.T) { - reset() - _, err := getValueDescriptorsByDeviceName(context.Background(), "error", logger.NewMockClient(), nil, dataMocks.NewMockDeviceClient()) - if err == nil { - t.Errorf("Expected error getting value descriptor by device name with some error") - } -} - -func TestGetValueDescriptorsByDeviceId(t *testing.T) { - reset() - _, err := getValueDescriptorsByDeviceId(context.Background(), "valid", logger.NewMockClient(), nil, dataMocks.NewMockDeviceClient()) - if err != nil { - t.Errorf("Unexpected error getting value descriptor by device id") - } -} - -func TestGetValueDescriptorsByDeviceIdNotFound(t *testing.T) { - reset() - _, err := getValueDescriptorsByDeviceId(context.Background(), "404", logger.NewMockClient(), nil, dataMocks.NewMockDeviceClient()) - if err != nil { - switch err := err.(type) { - case types.ErrServiceClient: - if err.StatusCode != http.StatusNotFound { - t.Errorf("Expected a 404 error") - } - return - default: - t.Errorf("Unexpected error getting value descriptor by device id missing in DB") - } - } - - if err == nil { - t.Errorf("Expected error getting value descriptor by device name missing in DB") - } -} - -func TestGetValueDescriptorsByDeviceIdError(t *testing.T) { - reset() - _, err := getValueDescriptorsByDeviceId(context.Background(), "error", logger.NewMockClient(), nil, dataMocks.NewMockDeviceClient()) - if err == nil { - t.Errorf("Expected error getting value descriptor by device id with some error") - } -} - -func TestGetAllValueDescriptors(t *testing.T) { - reset() - vds := []models.ValueDescriptor{ - {Id: testUUIDString}, - {Id: testBsonString}, - } - - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ValueDescriptors").Return(vds, nil) - _, err := getAllValueDescriptors(logger.NewMockClient(), dbClientMock) - if err != nil { - t.Errorf("Unexpected error getting all value descriptors") - } -} - -func TestGetAllValueDescriptorsError(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ValueDescriptors").Return([]models.ValueDescriptor{}, fmt.Errorf("some error")) - _, err := getAllValueDescriptors(logger.NewMockClient(), dbClientMock) - if err == nil { - t.Errorf("Expected error getting all value descriptors some error") - } -} - -func TestAddValueDescriptor(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("AddValueDescriptor", mock.Anything).Return("", nil) - _, err := addValueDescriptor(models.ValueDescriptor{Name: "valid"}, logger.NewMockClient(), dbClientMock) - if err != nil { - t.Errorf("Unexpected error adding value descriptor") - } -} - -func TestAddDuplicateValueDescriptor(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("AddValueDescriptor", mock.Anything).Return("", db.ErrNotUnique) - _, err := addValueDescriptor(models.ValueDescriptor{Name: "409"}, logger.NewMockClient(), dbClientMock) - if err != nil { - switch err.(type) { - case errors.ErrDuplicateValueDescriptorName: - return - default: - t.Errorf("Unexpected error adding value descriptor that already exists") - } - } - - if err == nil { - t.Errorf("Expected error adding value descriptor that already exists") - } -} - -func TestAddValueDescriptorError(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("AddValueDescriptor", mock.Anything).Return("", fmt.Errorf("some error")) - _, err := addValueDescriptor(models.ValueDescriptor{}, logger.NewMockClient(), dbClientMock) - if err == nil { - t.Errorf("Expected error adding value descriptor some error") - } -} - -func TestDeleteValueDescriptor(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("DeleteValueDescriptorById", mock.Anything).Return(nil) - dbClientMock.On("ReadingsByValueDescriptor", mock.Anything, mock.Anything).Return([]models.Reading{}, nil) - err := deleteValueDescriptor(models.ValueDescriptor{Name: "valid", Id: testBsonString}, logger.NewMockClient(), dbClientMock) - if err != nil { - t.Errorf("Unexpected error deleting value descriptor") - } -} - -func TestDeleteValueDescriptorInUse(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ReadingsByValueDescriptor", mock.Anything, mock.Anything).Return([]models.Reading{{Id: testUUIDString}}, nil) - err := deleteValueDescriptor(models.ValueDescriptor{Name: "409"}, logger.NewMockClient(), dbClientMock) - if err != nil { - switch err.(type) { - case errors.ErrValueDescriptorInUse: - return - default: - t.Errorf("Unexpected error deleting value descriptor in use") - } - } - - if err == nil { - t.Errorf("Expected error deleting value descriptor in use") - } -} - -func TestDeleteValueDescriptorErrorReadingsLookup(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ReadingsByValueDescriptor", mock.Anything, mock.Anything).Return([]models.Reading{}, fmt.Errorf("some error")) - err := deleteValueDescriptor(models.ValueDescriptor{}, logger.NewMockClient(), dbClientMock) - if err == nil { - t.Errorf("Expected error deleting value descriptor some error looking up readings") - } -} - -func TestDeleteValueDescriptorError(t *testing.T) { - reset() - dbClientMock := &mocks.DBClient{} - dbClientMock.On("ReadingsByValueDescriptor", mock.Anything, mock.Anything).Return([]models.Reading{}, nil) - dbClientMock.On("DeleteValueDescriptorById", mock.Anything).Return(fmt.Errorf("some error")) - err := deleteValueDescriptor(models.ValueDescriptor{Name: "validErrorTest"}, logger.NewMockClient(), dbClientMock) - if err == nil { - t.Errorf("Expected error deleting value descriptor some error") - } -} - -type closingBuffer struct { - *bytes.Buffer -} - -func (cb *closingBuffer) Close() (err error) { - return nil -} diff --git a/internal/core/metadata/README.md b/internal/core/metadata/README.md index 88854c1b25..91babad042 100644 --- a/internal/core/metadata/README.md +++ b/internal/core/metadata/README.md @@ -20,7 +20,7 @@ cd $GOPATH/src/github.com/edgexfoundry/edgex-go # pull the 3rd party / vendor packages make prepare # build the microservice -make cmd/core-metadata/core-metadata +make core-metadata # get to the core metadata microservice executable cd cmd/core-metadata # run the microservice (may require other dependent services to run correctly) diff --git a/internal/core/metadata/container/coredata.go b/internal/core/metadata/container/coredata.go deleted file mode 100644 index db4adaa018..0000000000 --- a/internal/core/metadata/container/coredata.go +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 container - -import ( - "github.com/edgexfoundry/go-mod-bootstrap/v2/di" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/coredata" -) - -// CoreDataValueDescriptorClientName contains the name of the CoreDataValueDescriptorClient's implementation in the DIC. -var CoreDataValueDescriptorClientName = di.TypeInstanceToName((*coredata.ValueDescriptorClient)(nil)) - -// CoreDataValueDescriptorClientFrom helper function queries the DIC and returns the CoreDataValueDescriptorClient's implementation. -func CoreDataValueDescriptorClientFrom(get di.Get) coredata.ValueDescriptorClient { - return get(CoreDataValueDescriptorClientName).(coredata.ValueDescriptorClient) -} diff --git a/internal/core/metadata/container/notifications.go b/internal/core/metadata/container/notifications.go deleted file mode 100644 index 8d2d9e1b23..0000000000 --- a/internal/core/metadata/container/notifications.go +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 container - -import ( - "github.com/edgexfoundry/go-mod-bootstrap/v2/di" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/notifications" -) - -// NotificationsClientName contains the name of the NotificationsClient's implementation in the DIC. -var NotificationsClientName = di.TypeInstanceToName((*notifications.NotificationsClient)(nil)) - -// NotificationsClientFrom helper function queries the DIC and returns the NotificationsClient's implementation. -func NotificationsClientFrom(get di.Get) notifications.NotificationsClient { - return get(NotificationsClientName).(notifications.NotificationsClient) -} diff --git a/internal/core/metadata/errors/types.go b/internal/core/metadata/errors/types.go deleted file mode 100644 index 1b13c2d35c..0000000000 --- a/internal/core/metadata/errors/types.go +++ /dev/null @@ -1,197 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Inc. - * - * 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 errors - -import ( - "fmt" -) - -type ErrLimitExceeded struct { - limit int -} - -func (e ErrLimitExceeded) Error() string { - return fmt.Sprintf("result count exceeds configured max %d", e.limit) -} - -func NewErrLimitExceeded(limit int) error { - return ErrLimitExceeded{limit: limit} -} - -type ErrDuplicateName struct { - msg string -} - -func (e ErrDuplicateName) Error() string { - return e.msg -} - -func NewErrDuplicateName(message string) error { - return ErrDuplicateName{msg: message} -} - -type ErrEmptyAddressableName struct { -} - -func (e ErrEmptyAddressableName) Error() string { - return "name is required for addressable" -} - -func NewErrEmptyAddressableName() error { - return ErrEmptyAddressableName{} -} - -type ErrAddressableNotFound struct { - id string - name string -} - -func (e ErrAddressableNotFound) Error() string { - if e.id != "" { - return fmt.Sprintf("addressable with id '%s' not found", e.id) - } else if e.name != "" { - return fmt.Sprintf("addressable with name '%s' not found", e.name) - } - return "addressable not found" -} - -func NewErrAddressableNotFound(id string, name string) error { - return ErrAddressableNotFound{id: id} -} - -type ErrAddressableInUse struct { - name string -} - -func (e ErrAddressableInUse) Error() string { - return fmt.Sprintf("addressable '%s' still in use", e.name) -} - -func NewErrAddressableInUse(name string) error { - return ErrAddressableInUse{name: name} -} - -type ErrBadRequest struct { - value string -} - -func (e ErrBadRequest) Error() string { - return fmt.Sprintf("received value %v is invalid", e.value) -} - -func NewErrBadRequest(invalid string) error { - return ErrBadRequest{value: invalid} -} - -type ErrItemNotFound struct { - key string -} - -func (e ErrItemNotFound) Error() string { - return fmt.Sprintf("no item found for supplied key: %s", e.key) -} - -func NewErrItemNotFound(key string) error { - return ErrItemNotFound{key: key} -} - -type ErrDeviceProfileNotFound struct { - name string - id string -} - -func (e ErrDeviceProfileNotFound) Error() string { - return fmt.Sprintf("device profile not found -- id: '%s' name: '%s'", e.id, e.name) -} - -func NewErrDeviceProfileNotFound(id string, name string) error { - return ErrDeviceProfileNotFound{ - id: id, - name: name, - } -} - -type ErrDeviceProfileInvalidState struct { - id string - name string - description string -} - -func (e ErrDeviceProfileInvalidState) Error() string { - return fmt.Sprintf("device profile invalid state -- id: '%s' name: '%s' description: '%s'", e.id, e.name, e.description) -} - -func NewErrDeviceProfileInvalidState(id string, name string, description string) error { - return ErrDeviceProfileInvalidState{ - id: id, - name: name, - description: description, - } -} - -type ErrEmptyDeviceProfileName struct { -} - -func (e ErrEmptyDeviceProfileName) Error() string { - return fmt.Sprint("Device profile name cannot be empty") -} - -func NewErrEmptyDeviceProfileName() error { - return ErrEmptyDeviceProfileName{} -} - -type ErrEmptyFile struct { - fileType string -} - -func (e ErrEmptyFile) Error() string { - return fmt.Sprintf("%s file is empty", e.fileType) -} - -func NewErrEmptyFile(fileType string) ErrEmptyFile { - return ErrEmptyFile{ - fileType: fileType, - } -} - -type ErrNameCollision struct { - name string - fromID string - toID string -} - -func (e ErrNameCollision) Error() string { - return fmt.Sprintf("%s is used by two provision watchers: %s and %s", e.name, e.fromID, e.toID) -} - -func NewErrNameCollision(name, fromID, toID string) ErrNameCollision { - return ErrNameCollision{ - name: name, - fromID: fromID, - toID: toID, - } -} - -type ErrDeviceProfileMarshalJson struct { - msg string -} - -func (e ErrDeviceProfileMarshalJson) Error() string { - return e.msg -} - -func NewErrDeviceProfileMarshalJson(message string) error { - return ErrDeviceProfileMarshalJson{msg: message} -} diff --git a/internal/core/metadata/init.go b/internal/core/metadata/init.go index 4c63e29eef..5a34d96282 100644 --- a/internal/core/metadata/init.go +++ b/internal/core/metadata/init.go @@ -19,20 +19,9 @@ import ( "sync" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/urlclient/local" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/container" "github.com/edgexfoundry/edgex-go/internal/core/metadata/v2" - errorContainer "github.com/edgexfoundry/edgex-go/internal/pkg/container" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - - bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/container" "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/startup" "github.com/edgexfoundry/go-mod-bootstrap/v2/di" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/coredata" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/notifications" - "github.com/gorilla/mux" ) @@ -50,33 +39,7 @@ func NewBootstrap(router *mux.Router) *Bootstrap { // BootstrapHandler fulfills the BootstrapHandler contract and performs initialization needed by the metadata service. func (b *Bootstrap) BootstrapHandler(ctx context.Context, wg *sync.WaitGroup, _ startup.Timer, dic *di.Container) bool { - loadRestRoutes(b.router, dic) v2.LoadRestRoutes(b.router, dic) - // TODO: there is an outstanding known issue (https://github.com/edgexfoundry/edgex-go/issues/2462) - // that could be seemingly be solved by moving from JIT initialization of these external clients to static - // init on startup, like registryClient and configuration are initialized. - // Doing so would cover over the symptoms of the bug, but the root problem of server processing taking longer - // than the configured client time out would still be present. - // Until that problem is addressed by larger architectural changes, if you are experiencing a bug similar to - // https://github.com/edgexfoundry/edgex-go/issues/2421, the correct fix is to bump up the client timeout. - configuration := container.ConfigurationFrom(dic.Get) - - // add dependencies to container - dic.Update(di.ServiceConstructorMap{ - errorContainer.ErrorHandlerName: func(get di.Get) interface{} { - return errorconcept.NewErrorHandler(bootstrapContainer.LoggingClientFrom(get)) - }, - container.CoreDataValueDescriptorClientName: func(get di.Get) interface{} { - return coredata.NewValueDescriptorClient( - local.New(configuration.Clients[clients.CoreDataServiceKey].Url() + clients.ApiValueDescriptorRoute)) - }, - container.NotificationsClientName: func(get di.Get) interface{} { - return notifications.NewNotificationsClient( - local.New(configuration.Clients[clients.SupportNotificationsServiceKey].Url() + clients.ApiNotificationRoute)) - - }, - }) - return true } diff --git a/internal/core/metadata/interfaces/db.go b/internal/core/metadata/interfaces/db.go deleted file mode 100644 index cda48c1f5c..0000000000 --- a/internal/core/metadata/interfaces/db.go +++ /dev/null @@ -1,98 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * - * 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 interfaces - -import ( - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type DBClient interface { - CloseSession() - - // Device Report - GetAllDeviceReports() ([]contract.DeviceReport, error) - GetDeviceReportByDeviceName(n string) ([]contract.DeviceReport, error) - GetDeviceReportByName(n string) (contract.DeviceReport, error) - GetDeviceReportById(id string) (contract.DeviceReport, error) - AddDeviceReport(dr contract.DeviceReport) (string, error) - UpdateDeviceReport(dr contract.DeviceReport) error - GetDeviceReportsByAction(n string) ([]contract.DeviceReport, error) - DeleteDeviceReportById(id string) error - - // Device - UpdateDevice(d contract.Device) error - GetDeviceById(id string) (contract.Device, error) - GetDeviceByName(n string) (contract.Device, error) - GetAllDevices() ([]contract.Device, error) - GetDevicesByProfileId(pid string) ([]contract.Device, error) - GetDevicesByServiceId(sid string) ([]contract.Device, error) - GetDevicesWithLabel(l string) ([]contract.Device, error) - AddDevice(d contract.Device, commands []contract.Command) (string, error) - DeleteDeviceById(id string) error - - // Device Profile - UpdateDeviceProfile(dp contract.DeviceProfile) error - AddDeviceProfile(d contract.DeviceProfile) (string, error) - GetAllDeviceProfiles() ([]contract.DeviceProfile, error) - GetDeviceProfileById(id string) (contract.DeviceProfile, error) - DeleteDeviceProfileById(id string) error - GetDeviceProfilesByModel(m string) ([]contract.DeviceProfile, error) - GetDeviceProfilesWithLabel(l string) ([]contract.DeviceProfile, error) - GetDeviceProfilesByManufacturerModel(man string, mod string) ([]contract.DeviceProfile, error) - GetDeviceProfilesByManufacturer(man string) ([]contract.DeviceProfile, error) - GetDeviceProfileByName(n string) (contract.DeviceProfile, error) - - // Addressable - UpdateAddressable(a contract.Addressable) error - AddAddressable(a contract.Addressable) (string, error) - GetAddressableById(id string) (contract.Addressable, error) - GetAddressableByName(n string) (contract.Addressable, error) - GetAddressablesByTopic(t string) ([]contract.Addressable, error) - GetAddressablesByPort(p int) ([]contract.Addressable, error) - GetAddressablesByPublisher(p string) ([]contract.Addressable, error) - GetAddressablesByAddress(add string) ([]contract.Addressable, error) - GetAddressables() ([]contract.Addressable, error) - DeleteAddressableById(id string) error - - // Device service - UpdateDeviceService(ds contract.DeviceService) error - GetDeviceServicesByAddressableId(id string) ([]contract.DeviceService, error) - GetDeviceServicesWithLabel(l string) ([]contract.DeviceService, error) - GetDeviceServiceById(id string) (contract.DeviceService, error) - GetDeviceServiceByName(n string) (contract.DeviceService, error) - GetAllDeviceServices() ([]contract.DeviceService, error) - AddDeviceService(ds contract.DeviceService) (string, error) - DeleteDeviceServiceById(id string) error - - // Provision watcher - GetProvisionWatcherById(id string) (contract.ProvisionWatcher, error) - GetAllProvisionWatchers() ([]contract.ProvisionWatcher, error) - GetProvisionWatcherByName(n string) (contract.ProvisionWatcher, error) - GetProvisionWatchersByProfileId(id string) ([]contract.ProvisionWatcher, error) - GetProvisionWatchersByServiceId(id string) ([]contract.ProvisionWatcher, error) - GetProvisionWatchersByIdentifier(k string, v string) ([]contract.ProvisionWatcher, error) - AddProvisionWatcher(pw contract.ProvisionWatcher) (string, error) - UpdateProvisionWatcher(pw contract.ProvisionWatcher) error - DeleteProvisionWatcherById(id string) error - - // Command - GetAllCommands() ([]contract.Command, error) - GetCommandById(id string) (contract.Command, error) - GetCommandsByName(id string) ([]contract.Command, error) - GetCommandsByDeviceId(id string) ([]contract.Command, error) - GetCommandByNameAndDeviceId(cname string, did string) (contract.Command, error) - - // Scrub all metadata (only used in test) - ScrubMetadata() error -} diff --git a/internal/core/metadata/interfaces/mocks/DBClient.go b/internal/core/metadata/interfaces/mocks/DBClient.go deleted file mode 100644 index cd3a096942..0000000000 --- a/internal/core/metadata/interfaces/mocks/DBClient.go +++ /dev/null @@ -1,1239 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// DBClient is an autogenerated mock type for the DBClient type -type DBClient struct { - mock.Mock -} - -// AddAddressable provides a mock function with given fields: a -func (_m *DBClient) AddAddressable(a models.Addressable) (string, error) { - ret := _m.Called(a) - - var r0 string - if rf, ok := ret.Get(0).(func(models.Addressable) string); ok { - r0 = rf(a) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.Addressable) error); ok { - r1 = rf(a) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AddDevice provides a mock function with given fields: d, commands -func (_m *DBClient) AddDevice(d models.Device, commands []models.Command) (string, error) { - ret := _m.Called(d, commands) - - var r0 string - if rf, ok := ret.Get(0).(func(models.Device, []models.Command) string); ok { - r0 = rf(d, commands) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.Device, []models.Command) error); ok { - r1 = rf(d, commands) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AddDeviceProfile provides a mock function with given fields: d -func (_m *DBClient) AddDeviceProfile(d models.DeviceProfile) (string, error) { - ret := _m.Called(d) - - var r0 string - if rf, ok := ret.Get(0).(func(models.DeviceProfile) string); ok { - r0 = rf(d) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.DeviceProfile) error); ok { - r1 = rf(d) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AddDeviceReport provides a mock function with given fields: dr -func (_m *DBClient) AddDeviceReport(dr models.DeviceReport) (string, error) { - ret := _m.Called(dr) - - var r0 string - if rf, ok := ret.Get(0).(func(models.DeviceReport) string); ok { - r0 = rf(dr) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.DeviceReport) error); ok { - r1 = rf(dr) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AddDeviceService provides a mock function with given fields: ds -func (_m *DBClient) AddDeviceService(ds models.DeviceService) (string, error) { - ret := _m.Called(ds) - - var r0 string - if rf, ok := ret.Get(0).(func(models.DeviceService) string); ok { - r0 = rf(ds) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.DeviceService) error); ok { - r1 = rf(ds) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AddProvisionWatcher provides a mock function with given fields: pw -func (_m *DBClient) AddProvisionWatcher(pw models.ProvisionWatcher) (string, error) { - ret := _m.Called(pw) - - var r0 string - if rf, ok := ret.Get(0).(func(models.ProvisionWatcher) string); ok { - r0 = rf(pw) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.ProvisionWatcher) error); ok { - r1 = rf(pw) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// CloseSession provides a mock function with given fields: -func (_m *DBClient) CloseSession() { - _m.Called() -} - -// DeleteAddressableById provides a mock function with given fields: id -func (_m *DBClient) DeleteAddressableById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteDeviceById provides a mock function with given fields: id -func (_m *DBClient) DeleteDeviceById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteDeviceProfileById provides a mock function with given fields: id -func (_m *DBClient) DeleteDeviceProfileById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteDeviceReportById provides a mock function with given fields: id -func (_m *DBClient) DeleteDeviceReportById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteDeviceServiceById provides a mock function with given fields: id -func (_m *DBClient) DeleteDeviceServiceById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteProvisionWatcherById provides a mock function with given fields: id -func (_m *DBClient) DeleteProvisionWatcherById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// GetAddressableById provides a mock function with given fields: id -func (_m *DBClient) GetAddressableById(id string) (models.Addressable, error) { - ret := _m.Called(id) - - var r0 models.Addressable - if rf, ok := ret.Get(0).(func(string) models.Addressable); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Addressable) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressableByName provides a mock function with given fields: n -func (_m *DBClient) GetAddressableByName(n string) (models.Addressable, error) { - ret := _m.Called(n) - - var r0 models.Addressable - if rf, ok := ret.Get(0).(func(string) models.Addressable); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.Addressable) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressables provides a mock function with given fields: -func (_m *DBClient) GetAddressables() ([]models.Addressable, error) { - ret := _m.Called() - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func() []models.Addressable); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressablesByAddress provides a mock function with given fields: add -func (_m *DBClient) GetAddressablesByAddress(add string) ([]models.Addressable, error) { - ret := _m.Called(add) - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func(string) []models.Addressable); ok { - r0 = rf(add) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(add) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressablesByPort provides a mock function with given fields: p -func (_m *DBClient) GetAddressablesByPort(p int) ([]models.Addressable, error) { - ret := _m.Called(p) - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func(int) []models.Addressable); ok { - r0 = rf(p) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(p) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressablesByPublisher provides a mock function with given fields: p -func (_m *DBClient) GetAddressablesByPublisher(p string) ([]models.Addressable, error) { - ret := _m.Called(p) - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func(string) []models.Addressable); ok { - r0 = rf(p) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(p) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressablesByTopic provides a mock function with given fields: t -func (_m *DBClient) GetAddressablesByTopic(t string) ([]models.Addressable, error) { - ret := _m.Called(t) - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func(string) []models.Addressable); ok { - r0 = rf(t) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(t) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAllCommands provides a mock function with given fields: -func (_m *DBClient) GetAllCommands() ([]models.Command, error) { - ret := _m.Called() - - var r0 []models.Command - if rf, ok := ret.Get(0).(func() []models.Command); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Command) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAllDeviceProfiles provides a mock function with given fields: -func (_m *DBClient) GetAllDeviceProfiles() ([]models.DeviceProfile, error) { - ret := _m.Called() - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func() []models.DeviceProfile); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAllDeviceReports provides a mock function with given fields: -func (_m *DBClient) GetAllDeviceReports() ([]models.DeviceReport, error) { - ret := _m.Called() - - var r0 []models.DeviceReport - if rf, ok := ret.Get(0).(func() []models.DeviceReport); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceReport) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAllDeviceServices provides a mock function with given fields: -func (_m *DBClient) GetAllDeviceServices() ([]models.DeviceService, error) { - ret := _m.Called() - - var r0 []models.DeviceService - if rf, ok := ret.Get(0).(func() []models.DeviceService); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceService) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAllDevices provides a mock function with given fields: -func (_m *DBClient) GetAllDevices() ([]models.Device, error) { - ret := _m.Called() - - var r0 []models.Device - if rf, ok := ret.Get(0).(func() []models.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Device) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAllProvisionWatchers provides a mock function with given fields: -func (_m *DBClient) GetAllProvisionWatchers() ([]models.ProvisionWatcher, error) { - ret := _m.Called() - - var r0 []models.ProvisionWatcher - if rf, ok := ret.Get(0).(func() []models.ProvisionWatcher); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.ProvisionWatcher) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetCommandById provides a mock function with given fields: id -func (_m *DBClient) GetCommandById(id string) (models.Command, error) { - ret := _m.Called(id) - - var r0 models.Command - if rf, ok := ret.Get(0).(func(string) models.Command); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Command) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetCommandByNameAndDeviceId provides a mock function with given fields: cname, did -func (_m *DBClient) GetCommandByNameAndDeviceId(cname string, did string) (models.Command, error) { - ret := _m.Called(cname, did) - - var r0 models.Command - if rf, ok := ret.Get(0).(func(string, string) models.Command); ok { - r0 = rf(cname, did) - } else { - r0 = ret.Get(0).(models.Command) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(cname, did) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetCommandsByDeviceId provides a mock function with given fields: id -func (_m *DBClient) GetCommandsByDeviceId(id string) ([]models.Command, error) { - ret := _m.Called(id) - - var r0 []models.Command - if rf, ok := ret.Get(0).(func(string) []models.Command); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Command) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetCommandsByName provides a mock function with given fields: id -func (_m *DBClient) GetCommandsByName(id string) ([]models.Command, error) { - ret := _m.Called(id) - - var r0 []models.Command - if rf, ok := ret.Get(0).(func(string) []models.Command); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Command) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceById provides a mock function with given fields: id -func (_m *DBClient) GetDeviceById(id string) (models.Device, error) { - ret := _m.Called(id) - - var r0 models.Device - if rf, ok := ret.Get(0).(func(string) models.Device); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Device) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceByName provides a mock function with given fields: n -func (_m *DBClient) GetDeviceByName(n string) (models.Device, error) { - ret := _m.Called(n) - - var r0 models.Device - if rf, ok := ret.Get(0).(func(string) models.Device); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.Device) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfileById provides a mock function with given fields: id -func (_m *DBClient) GetDeviceProfileById(id string) (models.DeviceProfile, error) { - ret := _m.Called(id) - - var r0 models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) models.DeviceProfile); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.DeviceProfile) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfileByName provides a mock function with given fields: n -func (_m *DBClient) GetDeviceProfileByName(n string) (models.DeviceProfile, error) { - ret := _m.Called(n) - - var r0 models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) models.DeviceProfile); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.DeviceProfile) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfilesByManufacturer provides a mock function with given fields: man -func (_m *DBClient) GetDeviceProfilesByManufacturer(man string) ([]models.DeviceProfile, error) { - ret := _m.Called(man) - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) []models.DeviceProfile); ok { - r0 = rf(man) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(man) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfilesByManufacturerModel provides a mock function with given fields: man, mod -func (_m *DBClient) GetDeviceProfilesByManufacturerModel(man string, mod string) ([]models.DeviceProfile, error) { - ret := _m.Called(man, mod) - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func(string, string) []models.DeviceProfile); ok { - r0 = rf(man, mod) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(man, mod) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfilesByModel provides a mock function with given fields: m -func (_m *DBClient) GetDeviceProfilesByModel(m string) ([]models.DeviceProfile, error) { - ret := _m.Called(m) - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) []models.DeviceProfile); ok { - r0 = rf(m) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(m) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfilesWithLabel provides a mock function with given fields: l -func (_m *DBClient) GetDeviceProfilesWithLabel(l string) ([]models.DeviceProfile, error) { - ret := _m.Called(l) - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) []models.DeviceProfile); ok { - r0 = rf(l) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(l) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceReportByDeviceName provides a mock function with given fields: n -func (_m *DBClient) GetDeviceReportByDeviceName(n string) ([]models.DeviceReport, error) { - ret := _m.Called(n) - - var r0 []models.DeviceReport - if rf, ok := ret.Get(0).(func(string) []models.DeviceReport); ok { - r0 = rf(n) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceReport) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceReportById provides a mock function with given fields: id -func (_m *DBClient) GetDeviceReportById(id string) (models.DeviceReport, error) { - ret := _m.Called(id) - - var r0 models.DeviceReport - if rf, ok := ret.Get(0).(func(string) models.DeviceReport); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.DeviceReport) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceReportByName provides a mock function with given fields: n -func (_m *DBClient) GetDeviceReportByName(n string) (models.DeviceReport, error) { - ret := _m.Called(n) - - var r0 models.DeviceReport - if rf, ok := ret.Get(0).(func(string) models.DeviceReport); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.DeviceReport) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceReportsByAction provides a mock function with given fields: n -func (_m *DBClient) GetDeviceReportsByAction(n string) ([]models.DeviceReport, error) { - ret := _m.Called(n) - - var r0 []models.DeviceReport - if rf, ok := ret.Get(0).(func(string) []models.DeviceReport); ok { - r0 = rf(n) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceReport) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceServiceById provides a mock function with given fields: id -func (_m *DBClient) GetDeviceServiceById(id string) (models.DeviceService, error) { - ret := _m.Called(id) - - var r0 models.DeviceService - if rf, ok := ret.Get(0).(func(string) models.DeviceService); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.DeviceService) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceServiceByName provides a mock function with given fields: n -func (_m *DBClient) GetDeviceServiceByName(n string) (models.DeviceService, error) { - ret := _m.Called(n) - - var r0 models.DeviceService - if rf, ok := ret.Get(0).(func(string) models.DeviceService); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.DeviceService) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceServicesByAddressableId provides a mock function with given fields: id -func (_m *DBClient) GetDeviceServicesByAddressableId(id string) ([]models.DeviceService, error) { - ret := _m.Called(id) - - var r0 []models.DeviceService - if rf, ok := ret.Get(0).(func(string) []models.DeviceService); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceService) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceServicesWithLabel provides a mock function with given fields: l -func (_m *DBClient) GetDeviceServicesWithLabel(l string) ([]models.DeviceService, error) { - ret := _m.Called(l) - - var r0 []models.DeviceService - if rf, ok := ret.Get(0).(func(string) []models.DeviceService); ok { - r0 = rf(l) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceService) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(l) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDevicesByProfileId provides a mock function with given fields: pid -func (_m *DBClient) GetDevicesByProfileId(pid string) ([]models.Device, error) { - ret := _m.Called(pid) - - var r0 []models.Device - if rf, ok := ret.Get(0).(func(string) []models.Device); ok { - r0 = rf(pid) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Device) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(pid) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDevicesByServiceId provides a mock function with given fields: sid -func (_m *DBClient) GetDevicesByServiceId(sid string) ([]models.Device, error) { - ret := _m.Called(sid) - - var r0 []models.Device - if rf, ok := ret.Get(0).(func(string) []models.Device); ok { - r0 = rf(sid) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Device) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(sid) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDevicesWithLabel provides a mock function with given fields: l -func (_m *DBClient) GetDevicesWithLabel(l string) ([]models.Device, error) { - ret := _m.Called(l) - - var r0 []models.Device - if rf, ok := ret.Get(0).(func(string) []models.Device); ok { - r0 = rf(l) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Device) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(l) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetProvisionWatcherById provides a mock function with given fields: id -func (_m *DBClient) GetProvisionWatcherById(id string) (models.ProvisionWatcher, error) { - ret := _m.Called(id) - - var r0 models.ProvisionWatcher - if rf, ok := ret.Get(0).(func(string) models.ProvisionWatcher); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.ProvisionWatcher) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetProvisionWatcherByName provides a mock function with given fields: n -func (_m *DBClient) GetProvisionWatcherByName(n string) (models.ProvisionWatcher, error) { - ret := _m.Called(n) - - var r0 models.ProvisionWatcher - if rf, ok := ret.Get(0).(func(string) models.ProvisionWatcher); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.ProvisionWatcher) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetProvisionWatchersByIdentifier provides a mock function with given fields: k, v -func (_m *DBClient) GetProvisionWatchersByIdentifier(k string, v string) ([]models.ProvisionWatcher, error) { - ret := _m.Called(k, v) - - var r0 []models.ProvisionWatcher - if rf, ok := ret.Get(0).(func(string, string) []models.ProvisionWatcher); ok { - r0 = rf(k, v) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.ProvisionWatcher) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(k, v) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetProvisionWatchersByProfileId provides a mock function with given fields: id -func (_m *DBClient) GetProvisionWatchersByProfileId(id string) ([]models.ProvisionWatcher, error) { - ret := _m.Called(id) - - var r0 []models.ProvisionWatcher - if rf, ok := ret.Get(0).(func(string) []models.ProvisionWatcher); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.ProvisionWatcher) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetProvisionWatchersByServiceId provides a mock function with given fields: id -func (_m *DBClient) GetProvisionWatchersByServiceId(id string) ([]models.ProvisionWatcher, error) { - ret := _m.Called(id) - - var r0 []models.ProvisionWatcher - if rf, ok := ret.Get(0).(func(string) []models.ProvisionWatcher); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.ProvisionWatcher) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ScrubMetadata provides a mock function with given fields: -func (_m *DBClient) ScrubMetadata() error { - ret := _m.Called() - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateAddressable provides a mock function with given fields: a -func (_m *DBClient) UpdateAddressable(a models.Addressable) error { - ret := _m.Called(a) - - var r0 error - if rf, ok := ret.Get(0).(func(models.Addressable) error); ok { - r0 = rf(a) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateDevice provides a mock function with given fields: d -func (_m *DBClient) UpdateDevice(d models.Device) error { - ret := _m.Called(d) - - var r0 error - if rf, ok := ret.Get(0).(func(models.Device) error); ok { - r0 = rf(d) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateDeviceProfile provides a mock function with given fields: dp -func (_m *DBClient) UpdateDeviceProfile(dp models.DeviceProfile) error { - ret := _m.Called(dp) - - var r0 error - if rf, ok := ret.Get(0).(func(models.DeviceProfile) error); ok { - r0 = rf(dp) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateDeviceReport provides a mock function with given fields: dr -func (_m *DBClient) UpdateDeviceReport(dr models.DeviceReport) error { - ret := _m.Called(dr) - - var r0 error - if rf, ok := ret.Get(0).(func(models.DeviceReport) error); ok { - r0 = rf(dr) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateDeviceService provides a mock function with given fields: ds -func (_m *DBClient) UpdateDeviceService(ds models.DeviceService) error { - ret := _m.Called(ds) - - var r0 error - if rf, ok := ret.Get(0).(func(models.DeviceService) error); ok { - r0 = rf(ds) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateProvisionWatcher provides a mock function with given fields: pw -func (_m *DBClient) UpdateProvisionWatcher(pw models.ProvisionWatcher) error { - ret := _m.Called(pw) - - var r0 error - if rf, ok := ret.Get(0).(func(models.ProvisionWatcher) error); ok { - r0 = rf(pw) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/internal/core/metadata/main.go b/internal/core/metadata/main.go index 2f40b1f7d5..ceb2f49829 100644 --- a/internal/core/metadata/main.go +++ b/internal/core/metadata/main.go @@ -23,7 +23,6 @@ import ( "github.com/edgexfoundry/edgex-go/internal/core/metadata/config" "github.com/edgexfoundry/edgex-go/internal/core/metadata/container" v2MetadataContainer "github.com/edgexfoundry/edgex-go/internal/core/metadata/v2/bootstrap/container" - "github.com/edgexfoundry/edgex-go/internal/pkg/bootstrap/handlers/database" "github.com/edgexfoundry/edgex-go/internal/pkg/telemetry" v2Handlers "github.com/edgexfoundry/edgex-go/internal/pkg/v2/bootstrap/handlers" @@ -37,7 +36,7 @@ import ( "github.com/gorilla/mux" ) -func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router, readyStream chan<- bool) { +func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router) { startupTimer := startup.NewStartUpTimer(clients.CoreMetaDataServiceKey) // All common command-line flags have been moved to DefaultCommonFlags. Service specific flags can be add here, @@ -70,12 +69,10 @@ func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router, re dic, true, []interfaces.BootstrapHandler{ - database.NewDatabase(httpServer, configuration).BootstrapHandler, v2Handlers.NewDatabase(httpServer, configuration, v2MetadataContainer.DBClientInterfaceName).BootstrapHandler, // add v2 db client bootstrap handler NewBootstrap(router).BootstrapHandler, telemetry.BootstrapHandler, httpServer.BootstrapHandler, handlers.NewStartMessage(clients.CoreMetaDataServiceKey, edgex.Version).BootstrapHandler, - handlers.NewReady(httpServer, readyStream).BootstrapHandler, }) } diff --git a/internal/core/metadata/operators/addressable/add.go b/internal/core/metadata/operators/addressable/add.go deleted file mode 100644 index 5740b8c0b1..0000000000 --- a/internal/core/metadata/operators/addressable/add.go +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 addressable - -import ( - "fmt" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type AddExecutor interface { - Execute() (string, error) -} - -type addressAdd struct { - database AddressWriter - addressable contract.Addressable -} - -// This method adds the provided Addressable to the database. -func (op addressAdd) Execute() (id string, err error) { - if len(op.addressable.Name) == 0 { - err := errors.NewErrEmptyAddressableName() - return "", err - } - id, err = op.database.AddAddressable(op.addressable) - if err != nil { - if err == db.ErrNotUnique { - err = errors.NewErrDuplicateName(fmt.Sprintf("duplicate name for addressable: %s", op.addressable.Name)) - } - return "", err - } - - return id, nil -} - -// This factory method returns an executor used to add an addressable. -func NewAddExecutor(db AddressWriter, addressable contract.Addressable) AddExecutor { - return addressAdd{ - database: db, - addressable: addressable, - } -} diff --git a/internal/core/metadata/operators/addressable/add_test.go b/internal/core/metadata/operators/addressable/add_test.go deleted file mode 100644 index 59c6dd7aab..0000000000 --- a/internal/core/metadata/operators/addressable/add_test.go +++ /dev/null @@ -1,122 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 addressable - -import ( - "fmt" - "reflect" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/addressable/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/stretchr/testify/mock" -) - -var missingName = contract.Addressable{ - User: "User1", - Protocol: "http", - Id: "address #1", - HTTPMethod: "POST", -} - -var duplicate = contract.Addressable{ - User: "faker", - Name: "Not Good", - Protocol: "http", - Id: "duplicated", - HTTPMethod: "POST", -} - -func TestAddExecutor(t *testing.T) { - success := SuccessfulDatabaseResult[0] - - tests := []struct { - name string - mockDb AddressWriter - addr contract.Addressable - expectedResult string - expectedError bool - expectedErrorVal error - }{ - { - name: "Successful database call", - mockDb: createAddMockAddressWriter(nil, success.Id), - addr: success, - expectedResult: success.Id, - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "Unsuccessful database call", - mockDb: createAddMockAddressWriter(Error, ""), - addr: success, - expectedResult: "", - expectedError: true, - expectedErrorVal: Error, - }, - { - name: "Missing name field on new addressable", - mockDb: createAddMockAddressWriter(nil, missingName.Id), - addr: missingName, - expectedResult: "", - expectedError: true, - expectedErrorVal: errors.NewErrEmptyAddressableName(), - }, - { - name: "Duplicated addressable", - mockDb: createAddMockAddressWriter(Error, duplicate.Id), - addr: duplicate, - expectedResult: "", - expectedError: true, - expectedErrorVal: errors.NewErrDuplicateName(fmt.Sprintf("duplicate name for addressable: %s", duplicate.Name)), - }, - } - - for _, test := range tests { - t.Run(test.name, func(tt *testing.T) { - op := NewAddExecutor(test.mockDb, test.addr) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -func createAddMockAddressWriter(err error, id string) AddressWriter { - dbMock := mocks.AddressWriter{} - dbMock.On("AddAddressable", duplicate).Return(id, db.ErrNotUnique) - dbMock.On("AddAddressable", mock.Anything).Return(id, err) - return &dbMock -} diff --git a/internal/core/metadata/operators/addressable/db.go b/internal/core/metadata/operators/addressable/db.go deleted file mode 100644 index 5293063d58..0000000000 --- a/internal/core/metadata/operators/addressable/db.go +++ /dev/null @@ -1,41 +0,0 @@ -// addressable contains functionality for obtaining Addressable data. -package addressable - -import contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// AddressLoader provides functionality for obtaining Addressables. -type AddressLoader interface { - GetAddressables() ([]contract.Addressable, error) - GetAddressablesByAddress(address string) ([]contract.Addressable, error) - GetAddressablesByPublisher(p string) ([]contract.Addressable, error) - GetAddressablesByPort(p int) ([]contract.Addressable, error) - GetAddressablesByTopic(t string) ([]contract.Addressable, error) - GetAddressableByName(n string) (contract.Addressable, error) - GetAddressableById(id string) (contract.Addressable, error) -} - -// AddressWriter provides functionality for adding Addressables. -type AddressWriter interface { - AddAddressable(addressable contract.Addressable) (string, error) -} - -// AddressUpdater provides functionality for updating existing Addressables. -type AddressUpdater interface { - UpdateAddressable(addressable contract.Addressable) error - - // This method is needed for testing whether addressables are still in use. - GetDeviceServicesByAddressableId(id string) ([]contract.DeviceService, error) - - AddressLoader - AddressWriter -} - -// AddressDeleter provides functionality for removing existing Addressables. -type AddressDeleter interface { - DeleteAddressableById(id string) error - - // This method is needed for testing whether addressables are still in use. - GetDeviceServicesByAddressableId(id string) ([]contract.DeviceService, error) - - AddressLoader -} diff --git a/internal/core/metadata/operators/addressable/delete.go b/internal/core/metadata/operators/addressable/delete.go deleted file mode 100644 index 8605b07744..0000000000 --- a/internal/core/metadata/operators/addressable/delete.go +++ /dev/null @@ -1,87 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 addressable - -import ( - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type DeleteExecutor interface { - Execute() error -} - -type addressDelete struct { - database AddressDeleter - id string - name string -} - -// This method adds the provided Addressable to the database. -func (op addressDelete) Execute() error { - var addressable contract.Addressable - var err error - - // deleteById and deleteByName all use the deleteById database function, so abstract away the front end difference - if op.id != "" { - addressable, err = op.database.GetAddressableById(op.id) - if err == db.ErrNotFound { - err = errors.NewErrAddressableNotFound(op.id, "") - } - } else { - addressable, err = op.database.GetAddressableByName(op.name) - if err == db.ErrNotFound { - err = errors.NewErrAddressableNotFound("", op.name) - } - } - - if err != nil { - return err - } - - // Check device services - ds, err := op.database.GetDeviceServicesByAddressableId(addressable.Id) - if err != nil { - return err - } - if len(ds) > 0 { - return errors.NewErrAddressableInUse(addressable.Name) - } - - err = op.database.DeleteAddressableById(addressable.Id) - if err != nil { - return err - } - - return nil -} - -// This factory method returns an executor used to delete an addressable by ID. -func NewDeleteByIdExecutor(db AddressDeleter, id string) DeleteExecutor { - return addressDelete{ - database: db, - id: id, - } -} - -// This factory method returns an executor used to delete an addressable by name. -func NewDeleteByNameExecutor(db AddressDeleter, name string) DeleteExecutor { - return addressDelete{ - database: db, - name: name, - } -} diff --git a/internal/core/metadata/operators/addressable/delete_test.go b/internal/core/metadata/operators/addressable/delete_test.go deleted file mode 100644 index 612ac1b3c2..0000000000 --- a/internal/core/metadata/operators/addressable/delete_test.go +++ /dev/null @@ -1,245 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 addressable - -import ( - "testing" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/addressable/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/stretchr/testify/mock" -) - -func TestDeleteByIdExecutor(t *testing.T) { - success := SuccessfulDatabaseResult[0] - - tests := []struct { - testName string - mockDeleter AddressDeleter - id string - expectedError bool - expectedErrorVal error - }{ - { - testName: "Successful database call by ID", - mockDeleter: createMockDeleter([]mockOutline{ - {"GetAddressableById", success.Id, success, nil}, - {"GetDeviceServicesByAddressableId", mock.Anything, []contract.DeviceService{}, nil}, - {"DeleteAddressableById", mock.Anything, nil, nil}, - }), - id: success.Id, - expectedError: false, - expectedErrorVal: nil, - }, - { - testName: "Addressable not found", - mockDeleter: createMockDeleter([]mockOutline{ - {"GetAddressableById", success.Id, contract.Addressable{}, db.ErrNotFound}, - }), - id: success.Id, - expectedError: true, - expectedErrorVal: errors.NewErrAddressableNotFound(success.Id, ""), - }, - { - testName: "No ID provided", - mockDeleter: createMockDeleter([]mockOutline{ - {"GetAddressableByName", "", contract.Addressable{}, db.ErrNotFound}, - }), - id: "", - expectedError: true, - expectedErrorVal: errors.NewErrAddressableNotFound("", ""), - }, - { - testName: "Unsuccessful database call retrieving addressable", - mockDeleter: createMockDeleter([]mockOutline{ - {"GetAddressableById", mock.Anything, contract.Addressable{}, Error}, - }), - id: success.Id, - expectedError: true, - expectedErrorVal: Error, - }, - { - testName: "Addressable in use", - mockDeleter: createMockDeleter([]mockOutline{ - {"GetAddressableById", success.Id, success, nil}, - {"GetDeviceServicesByAddressableId", mock.Anything, []contract.DeviceService{{}}, nil}, - {"DeleteAddressableById", mock.Anything, nil, nil}}), - id: success.Id, - expectedError: true, - expectedErrorVal: errors.NewErrAddressableInUse(success.Name), - }, - { - testName: "Unsuccessful database call retrieving device services", - mockDeleter: createMockDeleter([]mockOutline{ - {"GetAddressableById", success.Id, success, nil}, - {"GetDeviceServicesByAddressableId", mock.Anything, []contract.DeviceService{}, Error}, - }), - id: success.Id, - expectedError: true, - expectedErrorVal: Error, - }, - { - testName: "Unsuccessful database call deleting addressable", - mockDeleter: createMockDeleter([]mockOutline{ - {"GetAddressableById", success.Id, success, nil}, - {"GetDeviceServicesByAddressableId", mock.Anything, []contract.DeviceService{}, nil}, - {"DeleteAddressableById", mock.Anything, Error, nil}, - }), - id: success.Id, - expectedError: true, - expectedErrorVal: Error, - }, - } - - for _, test := range tests { - t.Run(test.testName, func(tt *testing.T) { - op := NewDeleteByIdExecutor(test.mockDeleter, test.id) - err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -func TestDeleteByNameExecutor(t *testing.T) { - success := SuccessfulDatabaseResult[0] - - tests := []struct { - testName string - mockDeleter AddressDeleter - name string - expectedError bool - expectedErrorVal error - }{ - { - testName: "Successful database call by name", - mockDeleter: createMockDeleter([]mockOutline{ - {"GetAddressableByName", success.Name, success, nil}, - {"GetDeviceServicesByAddressableId", mock.Anything, []contract.DeviceService{}, nil}, - {"DeleteAddressableById", mock.Anything, nil, nil}, - }), - name: success.Name, - expectedError: false, - expectedErrorVal: nil, - }, - { - testName: "Addressable not found", - mockDeleter: createMockDeleter([]mockOutline{ - {"GetAddressableByName", success.Name, contract.Addressable{}, db.ErrNotFound}, - }), - name: success.Name, - expectedError: true, - expectedErrorVal: errors.NewErrAddressableNotFound("", success.Name), - }, - { - testName: "No name provided", - mockDeleter: createMockDeleter([]mockOutline{ - {"GetAddressableByName", "", contract.Addressable{}, db.ErrNotFound}, - }), - name: "", - expectedError: true, - expectedErrorVal: errors.NewErrAddressableNotFound("", ""), - }, - { - testName: "Unsuccessful database call retrieving addressable", - mockDeleter: createMockDeleter([]mockOutline{ - {"GetAddressableByName", mock.Anything, contract.Addressable{}, Error}, - }), - name: success.Name, - expectedError: true, - expectedErrorVal: Error, - }, - { - testName: "Addressable in use", - mockDeleter: createMockDeleter([]mockOutline{ - {"GetAddressableByName", mock.Anything, success, nil}, - {"GetDeviceServicesByAddressableId", mock.Anything, []contract.DeviceService{{}}, nil}, - {"DeleteAddressableById", mock.Anything, nil, nil}, - }), - name: success.Name, - expectedError: true, - expectedErrorVal: errors.NewErrAddressableInUse(success.Name), - }, - { - testName: "Unsuccessful database call retrieving device services", - mockDeleter: createMockDeleter([]mockOutline{ - {"GetAddressableByName", mock.Anything, contract.Addressable{}, Error}, - {"GetDeviceServicesByAddressableId", mock.Anything, []contract.DeviceService{}, Error}, - }), - name: success.Name, - expectedError: true, - expectedErrorVal: Error, - }, - { - testName: "Unsuccessful database call deleting addressable", - mockDeleter: createMockDeleter([]mockOutline{ - {"GetAddressableByName", mock.Anything, contract.Addressable{}, Error}, - {"GetDeviceServicesByAddressableId", mock.Anything, []contract.DeviceService{}, nil}, - {"DeleteAddressableById", mock.Anything, Error, nil}, - }), - name: success.Name, - expectedError: true, - expectedErrorVal: Error, - }, - } - - for _, test := range tests { - t.Run(test.testName, func(tt *testing.T) { - op := NewDeleteByNameExecutor(test.mockDeleter, test.name) - err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -func createMockDeleter(outlines []mockOutline) AddressDeleter { - dbMock := mocks.AddressDeleter{} - - for _, o := range outlines { - dbMock.On(o.methodName, o.arg).Return(o.ret, o.err) - } - - return &dbMock -} diff --git a/internal/core/metadata/operators/addressable/get.go b/internal/core/metadata/operators/addressable/get.go deleted file mode 100644 index 238e0bf296..0000000000 --- a/internal/core/metadata/operators/addressable/get.go +++ /dev/null @@ -1,186 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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. - *******************************************************************************/ - -// addressable contains functionality for obtaining Addressable data. -package addressable - -import ( - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type AddressableAllExecutor interface { - Execute() ([]contract.Addressable, error) -} - -type addressableLoadAll struct { - config bootstrapConfig.ServiceInfo - database AddressLoader - logger logger.LoggingClient -} - -func NewAddressableLoadAll(cfg bootstrapConfig.ServiceInfo, db AddressLoader, log logger.LoggingClient) AddressableAllExecutor { - return addressableLoadAll{config: cfg, database: db, logger: log} -} - -func (op addressableLoadAll) Execute() (addressables []contract.Addressable, err error) { - addressables, err = op.database.GetAddressables() - if err != nil { - op.logger.Error(err.Error()) - return nil, err - } - if len(addressables) > op.config.MaxResultCount { - err = errors.NewErrLimitExceeded(op.config.MaxResultCount) - op.logger.Error(err.Error()) - return nil, err - } - return -} - -type IdExecutor interface { - Execute() (contract.Addressable, error) -} - -type addressLoadById struct { - database AddressLoader - id string -} - -func (op addressLoadById) Execute() (contract.Addressable, error) { - return op.database.GetAddressableById(op.id) -} - -func NewIdExecutor(db AddressLoader, id string) IdExecutor { - return addressLoadById{ - database: db, - id: id, - } -} - -type NameExecutor interface { - Execute() (contract.Addressable, error) -} - -type addressLoadByName struct { - database AddressLoader - name string -} - -func (op addressLoadByName) Execute() (contract.Addressable, error) { - return op.database.GetAddressableByName(op.name) -} - -func NewNameExecutor(db AddressLoader, name string) NameExecutor { - return addressLoadByName{ - database: db, - name: name, - } -} - -// AddressExecutor provides functionality for retrieving addresses from an underlying data-store. -type AddressExecutor interface { - Execute() ([]contract.Addressable, error) -} - -// addressLoadByAddress loads address by way of the operator pattern. -type addressLoadByAddress struct { - database AddressLoader - address string -} - -// Execute retrieves the addressables from the underlying data-store. -func (n addressLoadByAddress) Execute() ([]contract.Addressable, error) { - return n.database.GetAddressablesByAddress(n.address) -} - -// NewAddressExecutor creates an AddressExecutor. -func NewAddressExecutor(db AddressLoader, address string) AddressExecutor { - return addressLoadByAddress{ - database: db, - address: address} -} - -// PublisherExecutor provides functionality for retrieving addresses from an underlying data-store. -type PublisherExecutor interface { - Execute() ([]contract.Addressable, error) -} - -// addressLoadByPublisher loads address by way of the operator pattern. -type addressLoadByPublisher struct { - database AddressLoader - publisher string -} - -// Execute retrieves the addressables from the underlying data-store. -func (p addressLoadByPublisher) Execute() ([]contract.Addressable, error) { - return p.database.GetAddressablesByPublisher(p.publisher) -} - -// NewPublisherExecutor creates a PublisherExecutor. -func NewPublisherExecutor(db AddressLoader, publisher string) PublisherExecutor { - return addressLoadByPublisher{ - database: db, - publisher: publisher, - } -} - -// PortExecutor provides functionality for retrieving addresses from an underlying data-store. -type PortExecutor interface { - Execute() ([]contract.Addressable, error) -} - -// addressByPortLoader loads address by way of the operator pattern. -type addressByPortLoader struct { - database AddressLoader - port int -} - -// Execute retrieves the addressables from the underlying data-store. -func (n addressByPortLoader) Execute() ([]contract.Addressable, error) { - return n.database.GetAddressablesByPort(n.port) -} - -// NewPortExecutor creates a PortExecutor. -func NewPortExecutor(db AddressLoader, port int) PortExecutor { - return addressByPortLoader{ - database: db, - port: port} -} - -// TopicExecutor provides functionality for retrieving addresses from an underlying data-store. -type TopicExecutor interface { - Execute() ([]contract.Addressable, error) -} - -// addressByTopicLoader loads address by way of the operator pattern. -type addressByTopicLoader struct { - database AddressLoader - topic string -} - -// Execute retrieves the addressables from the underlying data-store. -func (n addressByTopicLoader) Execute() ([]contract.Addressable, error) { - return n.database.GetAddressablesByTopic(n.topic) -} - -// NewTopicExecutor creates a TopicExecutor. -func NewTopicExecutor(db AddressLoader, topic string) TopicExecutor { - return addressByTopicLoader{ - database: db, - topic: topic} -} diff --git a/internal/core/metadata/operators/addressable/get_test.go b/internal/core/metadata/operators/addressable/get_test.go deleted file mode 100644 index eecc1edbb7..0000000000 --- a/internal/core/metadata/operators/addressable/get_test.go +++ /dev/null @@ -1,380 +0,0 @@ -package addressable - -import ( - "reflect" - "testing" - - metadataErrors "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/addressable/mocks" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/pkg/errors" -) - -var Id = "83cb038b-5a94-4707-985d-13effec62de2" -var AddressName = "TestAddress" -var PublisherName = "TestPublisher" -var Topic = "TestTopic" -var Error = errors.New("test error") -var Port = 8080 -var SuccessfulDatabaseResult = []contract.Addressable{ - { - User: "User1", - Name: "Ein", - Protocol: "http", - Id: "address #1", - HTTPMethod: "POST", - Address: "localhost", - Port: 1337, - Path: "/tests", - Publisher: "Brandon!", - Password: "hunter2", - Topic: "Hot", - }, - { - User: "User2", - Name: "Zwei", - Protocol: "http", - Id: "address #2", - HTTPMethod: "GET", - Address: "localhost", - Port: 1337, - Path: "/tests", - Publisher: "Brandon!", - Password: "hunter2", - Topic: "Hot", - }, -} - -func TestAddressExecutor(t *testing.T) { - tests := []struct { - name string - mockDb AddressLoader - expectedResult []contract.Addressable - expectedError bool - }{ - { - name: "Successful database call", - mockDb: createMockAddressLoaderStringArg("GetAddressablesByAddress", nil, SuccessfulDatabaseResult, AddressName), - expectedResult: SuccessfulDatabaseResult, - expectedError: false, - }, - { - name: "Error database result", - mockDb: createMockAddressLoaderStringArg("GetAddressablesByAddress", Error, nil, AddressName), - expectedResult: nil, - expectedError: true, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewAddressExecutor(test.mockDb, AddressName) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed. \nExpected : %v \n Observed: %v", test.expectedResult, actual) - return - } - }) - } -} - -func TestAllAddressablesExecutor(t *testing.T) { - tests := []struct { - name string - mockDb AddressLoader - cfg bootstrapConfig.ServiceInfo - expectedResult []contract.Addressable - expectedError bool - expectedErrorVal error - }{ - { - name: "Successful database call", - mockDb: createMockAddressLoader(nil, SuccessfulDatabaseResult), - cfg: bootstrapConfig.ServiceInfo{MaxResultCount: 5}, - expectedResult: SuccessfulDatabaseResult, - expectedError: false, - }, - { - name: "Unsuccessful database call", - mockDb: createMockAddressLoader(Error, nil), - cfg: bootstrapConfig.ServiceInfo{MaxResultCount: 5}, - expectedResult: nil, - expectedError: true, - }, - { - name: "MaxResultCount exceeded", - mockDb: createMockAddressLoader(nil, SuccessfulDatabaseResult), - cfg: bootstrapConfig.ServiceInfo{MaxResultCount: 1}, - expectedResult: nil, - expectedError: true, - expectedErrorVal: metadataErrors.NewErrLimitExceeded(1), - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewAddressableLoadAll(test.cfg, test.mockDb, logger.NewMockClient()) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - if !test.expectedError && err != nil { - t.Error("Unexpected error") - return - } - if !reflect.DeepEqual(actual, test.expectedResult) { - t.Errorf("Observed result doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedResult, actual) - } - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -func TestNameExecutor(t *testing.T) { - tests := []struct { - name string - mockDb AddressLoader - expectedResult contract.Addressable - expectedError bool - }{ - { - name: "Successful database call", - mockDb: createMockAddressLoaderStringArg("GetAddressableByName", nil, SuccessfulDatabaseResult[0], AddressName), - expectedResult: SuccessfulDatabaseResult[0], - expectedError: false, - }, - { - name: "Unsuccessful database call", - mockDb: createMockAddressLoaderStringArg("GetAddressableByName", Error, contract.Addressable{}, AddressName), - expectedResult: contract.Addressable{}, - expectedError: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewNameExecutor(test.mockDb, AddressName) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func TestIdExecutor(t *testing.T) { - tests := []struct { - name string - mockDb AddressLoader - expectedResult contract.Addressable - expectedError bool - }{ - { - name: "Successful database call", - mockDb: createMockAddressLoaderStringArg("GetAddressableById", nil, SuccessfulDatabaseResult[0], Id), - expectedResult: SuccessfulDatabaseResult[0], - expectedError: false, - }, - { - name: "Unsuccessful database call", - mockDb: createMockAddressLoaderStringArg("GetAddressableById", Error, contract.Addressable{}, Id), - expectedResult: contract.Addressable{}, - expectedError: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewIdExecutor(test.mockDb, Id) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func TestPublisherExecutor(t *testing.T) { - tests := []struct { - name string - mockDb AddressLoader - expectedResult []contract.Addressable - expectedError bool - }{ - { - name: "Successful database call", - mockDb: createMockAddressLoaderStringArg("GetAddressablesByPublisher", nil, SuccessfulDatabaseResult, PublisherName), - expectedResult: SuccessfulDatabaseResult, - expectedError: false, - }, - { - name: "Error database result", - mockDb: createMockAddressLoaderStringArg("GetAddressablesByPublisher", Error, nil, PublisherName), - expectedResult: nil, - expectedError: true, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewPublisherExecutor(test.mockDb, PublisherName) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed. \nExpected : %v \n Observed: %v", test.expectedResult, actual) - return - } - }) - } -} - -func TestPortExecutor(t *testing.T) { - tests := []struct { - name string - mockDb AddressLoader - expectedResult []contract.Addressable - expectedError bool - }{ - { - name: "Successful database call", - mockDb: createMockAddressLoaderIntArg("GetAddressablesByPort", nil, SuccessfulDatabaseResult, Port), - expectedResult: SuccessfulDatabaseResult, - expectedError: false, - }, - { - name: "Error database result", - mockDb: createMockAddressLoaderIntArg("GetAddressablesByPort", Error, nil, Port), - expectedResult: nil, - expectedError: true, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewPortExecutor(test.mockDb, Port) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed. \nExpected : %v \n Observed: %v", test.expectedResult, actual) - return - } - }) - } -} - -func TestTopicExecutor(t *testing.T) { - tests := []struct { - name string - mockDb AddressLoader - expectedResult []contract.Addressable - expectedError bool - }{ - { - name: "Successful database call", - mockDb: createMockAddressLoaderStringArg("GetAddressablesByTopic", nil, SuccessfulDatabaseResult, Topic), - expectedResult: SuccessfulDatabaseResult, - expectedError: false, - }, - { - name: "Error database result", - mockDb: createMockAddressLoaderStringArg("GetAddressablesByTopic", Error, nil, Topic), - expectedResult: nil, - expectedError: true, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewTopicExecutor(test.mockDb, Topic) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed. \nExpected : %v \n Observed: %v", test.expectedResult, actual) - return - } - }) - } -} - -func createMockAddressLoaderStringArg(methodName string, err error, ret interface{}, arg string) AddressLoader { - dbMock := mocks.AddressLoader{} - dbMock.On(methodName, arg).Return(ret, err) - return &dbMock -} - -func createMockAddressLoaderIntArg(methodName string, err error, ret interface{}, arg int) AddressLoader { - dbMock := mocks.AddressLoader{} - dbMock.On(methodName, arg).Return(ret, err) - return &dbMock -} - -func createMockAddressLoader(err error, ret []contract.Addressable) AddressLoader { - dbMock := mocks.AddressLoader{} - dbMock.On("GetAddressables").Return(ret, err) - return &dbMock -} diff --git a/internal/core/metadata/operators/addressable/mocks/AddressDeleter.go b/internal/core/metadata/operators/addressable/mocks/AddressDeleter.go deleted file mode 100644 index 285ad8b6e2..0000000000 --- a/internal/core/metadata/operators/addressable/mocks/AddressDeleter.go +++ /dev/null @@ -1,205 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// AddressDeleter is an autogenerated mock type for the AddressDeleter type -type AddressDeleter struct { - mock.Mock -} - -// DeleteAddressableById provides a mock function with given fields: id -func (_m *AddressDeleter) DeleteAddressableById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// GetAddressableById provides a mock function with given fields: id -func (_m *AddressDeleter) GetAddressableById(id string) (models.Addressable, error) { - ret := _m.Called(id) - - var r0 models.Addressable - if rf, ok := ret.Get(0).(func(string) models.Addressable); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Addressable) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressableByName provides a mock function with given fields: n -func (_m *AddressDeleter) GetAddressableByName(n string) (models.Addressable, error) { - ret := _m.Called(n) - - var r0 models.Addressable - if rf, ok := ret.Get(0).(func(string) models.Addressable); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.Addressable) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressables provides a mock function with given fields: -func (_m *AddressDeleter) GetAddressables() ([]models.Addressable, error) { - ret := _m.Called() - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func() []models.Addressable); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressablesByAddress provides a mock function with given fields: address -func (_m *AddressDeleter) GetAddressablesByAddress(address string) ([]models.Addressable, error) { - ret := _m.Called(address) - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func(string) []models.Addressable); ok { - r0 = rf(address) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(address) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressablesByPort provides a mock function with given fields: p -func (_m *AddressDeleter) GetAddressablesByPort(p int) ([]models.Addressable, error) { - ret := _m.Called(p) - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func(int) []models.Addressable); ok { - r0 = rf(p) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(p) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressablesByPublisher provides a mock function with given fields: p -func (_m *AddressDeleter) GetAddressablesByPublisher(p string) ([]models.Addressable, error) { - ret := _m.Called(p) - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func(string) []models.Addressable); ok { - r0 = rf(p) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(p) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressablesByTopic provides a mock function with given fields: t -func (_m *AddressDeleter) GetAddressablesByTopic(t string) ([]models.Addressable, error) { - ret := _m.Called(t) - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func(string) []models.Addressable); ok { - r0 = rf(t) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(t) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceServicesByAddressableId provides a mock function with given fields: id -func (_m *AddressDeleter) GetDeviceServicesByAddressableId(id string) ([]models.DeviceService, error) { - ret := _m.Called(id) - - var r0 []models.DeviceService - if rf, ok := ret.Get(0).(func(string) []models.DeviceService); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceService) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/metadata/operators/addressable/mocks/AddressLoader.go b/internal/core/metadata/operators/addressable/mocks/AddressLoader.go deleted file mode 100644 index 6fc38cc62d..0000000000 --- a/internal/core/metadata/operators/addressable/mocks/AddressLoader.go +++ /dev/null @@ -1,168 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// AddressLoader is an autogenerated mock type for the AddressLoader type -type AddressLoader struct { - mock.Mock -} - -// GetAddressableById provides a mock function with given fields: id -func (_m *AddressLoader) GetAddressableById(id string) (models.Addressable, error) { - ret := _m.Called(id) - - var r0 models.Addressable - if rf, ok := ret.Get(0).(func(string) models.Addressable); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Addressable) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressableByName provides a mock function with given fields: n -func (_m *AddressLoader) GetAddressableByName(n string) (models.Addressable, error) { - ret := _m.Called(n) - - var r0 models.Addressable - if rf, ok := ret.Get(0).(func(string) models.Addressable); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.Addressable) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressables provides a mock function with given fields: -func (_m *AddressLoader) GetAddressables() ([]models.Addressable, error) { - ret := _m.Called() - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func() []models.Addressable); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressablesByAddress provides a mock function with given fields: address -func (_m *AddressLoader) GetAddressablesByAddress(address string) ([]models.Addressable, error) { - ret := _m.Called(address) - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func(string) []models.Addressable); ok { - r0 = rf(address) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(address) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressablesByPort provides a mock function with given fields: p -func (_m *AddressLoader) GetAddressablesByPort(p int) ([]models.Addressable, error) { - ret := _m.Called(p) - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func(int) []models.Addressable); ok { - r0 = rf(p) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(p) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressablesByPublisher provides a mock function with given fields: p -func (_m *AddressLoader) GetAddressablesByPublisher(p string) ([]models.Addressable, error) { - ret := _m.Called(p) - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func(string) []models.Addressable); ok { - r0 = rf(p) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(p) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressablesByTopic provides a mock function with given fields: t -func (_m *AddressLoader) GetAddressablesByTopic(t string) ([]models.Addressable, error) { - ret := _m.Called(t) - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func(string) []models.Addressable); ok { - r0 = rf(t) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(t) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/metadata/operators/addressable/mocks/AddressUpdater.go b/internal/core/metadata/operators/addressable/mocks/AddressUpdater.go deleted file mode 100644 index b4d9e6f594..0000000000 --- a/internal/core/metadata/operators/addressable/mocks/AddressUpdater.go +++ /dev/null @@ -1,226 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// AddressUpdater is an autogenerated mock type for the AddressUpdater type -type AddressUpdater struct { - mock.Mock -} - -// AddAddressable provides a mock function with given fields: _a0 -func (_m *AddressUpdater) AddAddressable(_a0 models.Addressable) (string, error) { - ret := _m.Called(_a0) - - var r0 string - if rf, ok := ret.Get(0).(func(models.Addressable) string); ok { - r0 = rf(_a0) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.Addressable) error); ok { - r1 = rf(_a0) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressableById provides a mock function with given fields: id -func (_m *AddressUpdater) GetAddressableById(id string) (models.Addressable, error) { - ret := _m.Called(id) - - var r0 models.Addressable - if rf, ok := ret.Get(0).(func(string) models.Addressable); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Addressable) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressableByName provides a mock function with given fields: n -func (_m *AddressUpdater) GetAddressableByName(n string) (models.Addressable, error) { - ret := _m.Called(n) - - var r0 models.Addressable - if rf, ok := ret.Get(0).(func(string) models.Addressable); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.Addressable) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressables provides a mock function with given fields: -func (_m *AddressUpdater) GetAddressables() ([]models.Addressable, error) { - ret := _m.Called() - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func() []models.Addressable); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressablesByAddress provides a mock function with given fields: address -func (_m *AddressUpdater) GetAddressablesByAddress(address string) ([]models.Addressable, error) { - ret := _m.Called(address) - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func(string) []models.Addressable); ok { - r0 = rf(address) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(address) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressablesByPort provides a mock function with given fields: p -func (_m *AddressUpdater) GetAddressablesByPort(p int) ([]models.Addressable, error) { - ret := _m.Called(p) - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func(int) []models.Addressable); ok { - r0 = rf(p) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(p) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressablesByPublisher provides a mock function with given fields: p -func (_m *AddressUpdater) GetAddressablesByPublisher(p string) ([]models.Addressable, error) { - ret := _m.Called(p) - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func(string) []models.Addressable); ok { - r0 = rf(p) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(p) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressablesByTopic provides a mock function with given fields: t -func (_m *AddressUpdater) GetAddressablesByTopic(t string) ([]models.Addressable, error) { - ret := _m.Called(t) - - var r0 []models.Addressable - if rf, ok := ret.Get(0).(func(string) []models.Addressable); ok { - r0 = rf(t) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Addressable) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(t) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceServicesByAddressableId provides a mock function with given fields: id -func (_m *AddressUpdater) GetDeviceServicesByAddressableId(id string) ([]models.DeviceService, error) { - ret := _m.Called(id) - - var r0 []models.DeviceService - if rf, ok := ret.Get(0).(func(string) []models.DeviceService); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceService) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateAddressable provides a mock function with given fields: _a0 -func (_m *AddressUpdater) UpdateAddressable(_a0 models.Addressable) error { - ret := _m.Called(_a0) - - var r0 error - if rf, ok := ret.Get(0).(func(models.Addressable) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/internal/core/metadata/operators/addressable/mocks/AddressWriter.go b/internal/core/metadata/operators/addressable/mocks/AddressWriter.go deleted file mode 100644 index 9bc521ce25..0000000000 --- a/internal/core/metadata/operators/addressable/mocks/AddressWriter.go +++ /dev/null @@ -1,32 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// AddressWriter is an autogenerated mock type for the AddressWriter type -type AddressWriter struct { - mock.Mock -} - -// AddAddressable provides a mock function with given fields: _a0 -func (_m *AddressWriter) AddAddressable(_a0 models.Addressable) (string, error) { - ret := _m.Called(_a0) - - var r0 string - if rf, ok := ret.Get(0).(func(models.Addressable) string); ok { - r0 = rf(_a0) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.Addressable) error); ok { - r1 = rf(_a0) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/metadata/operators/addressable/update.go b/internal/core/metadata/operators/addressable/update.go deleted file mode 100644 index 05af1cebef..0000000000 --- a/internal/core/metadata/operators/addressable/update.go +++ /dev/null @@ -1,104 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 addressable - -import ( - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type UpdateExecutor interface { - Execute() error -} - -type addressUpdate struct { - updater AddressUpdater - addressable contract.Addressable -} - -// This method updates the provided Addressable in the database. -func (op addressUpdate) Execute() error { - var dest contract.Addressable - var err error - // Check if the addressable exists - if op.addressable.Id == "" { - dest, err = op.updater.GetAddressableByName(op.addressable.Name) - } else { - dest, err = op.updater.GetAddressableById(op.addressable.Id) - } - - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrAddressableNotFound(op.addressable.Id, op.addressable.Name) - } - return err - } - - // If the name is changed, check if the addressable is still in use - if op.addressable.Name != "" && op.addressable.Name != dest.Name { - // determine if the addressable is still in use - ds, err := op.updater.GetDeviceServicesByAddressableId(op.addressable.Id) - if err != nil { - return err - } - if len(ds) > 0 { - err = errors.NewErrAddressableInUse(dest.Name) - return err - } - } - - if op.addressable.Name != "" { - dest.Name = op.addressable.Name - } - if op.addressable.Protocol != "" { - dest.Protocol = op.addressable.Protocol - } - if op.addressable.Address != "" { - dest.Address = op.addressable.Address - } - if op.addressable.Port != 0 { - dest.Port = op.addressable.Port - } - if op.addressable.Path != "" { - dest.Path = op.addressable.Path - } - if op.addressable.Publisher != "" { - dest.Publisher = op.addressable.Publisher - } - if op.addressable.User != "" { - dest.User = op.addressable.User - } - if op.addressable.Password != "" { - dest.Password = op.addressable.Password - } - if op.addressable.Topic != "" { - dest.Topic = op.addressable.Topic - } - - if err := op.updater.UpdateAddressable(dest); err != nil { - return err - } - - return nil -} - -// This factory method returns an executor used to update an addressable. -func NewUpdateExecutor(updater AddressUpdater, addressable contract.Addressable) UpdateExecutor { - return addressUpdate{ - updater: updater, - addressable: addressable, - } -} diff --git a/internal/core/metadata/operators/addressable/update_test.go b/internal/core/metadata/operators/addressable/update_test.go deleted file mode 100644 index cc81cea22b..0000000000 --- a/internal/core/metadata/operators/addressable/update_test.go +++ /dev/null @@ -1,154 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 addressable - -import ( - "testing" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/addressable/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/stretchr/testify/mock" -) - -func TestUpdateExecutor(t *testing.T) { - successNoId := SuccessfulDatabaseResult[0] - successNoId.Name = successNoId.Id - successNoId.Id = "" - - successNewName := SuccessfulDatabaseResult[0] - successNewName.Name = "something different" - - tests := []struct { - name string - mockUpdater AddressUpdater - addr contract.Addressable - expectedError bool - expectedErrorVal error - }{ - { - name: "Successful database call, search by ID", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetAddressableById", SuccessfulDatabaseResult[0].Id, SuccessfulDatabaseResult[0], nil}, - {"UpdateAddressable", mock.Anything, nil, nil}}), - addr: SuccessfulDatabaseResult[0], - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "Successful database call, search by name", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetAddressableByName", successNoId.Name, successNoId, nil}, - {"UpdateAddressable", mock.Anything, nil, nil}}), - addr: successNoId, - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "Successful database call, updated name", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetAddressableById", SuccessfulDatabaseResult[0].Id, SuccessfulDatabaseResult[0], nil}, - {"UpdateAddressable", mock.Anything, nil, nil}, - {"GetDeviceServicesByAddressableId", mock.Anything, []contract.DeviceService{}, nil}}), - addr: successNewName, - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "Unsuccessful device service database call, updated name", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetAddressableById", SuccessfulDatabaseResult[0].Id, SuccessfulDatabaseResult[0], nil}, - {"GetDeviceServicesByAddressableId", mock.Anything, nil, Error}}), - addr: successNewName, - expectedError: true, - expectedErrorVal: Error, - }, - { - name: "Addressable in use", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetAddressableById", SuccessfulDatabaseResult[0].Id, SuccessfulDatabaseResult[0], nil}, - {"UpdateAddressable", mock.Anything, nil, nil}, - {"GetDeviceServicesByAddressableId", mock.Anything, []contract.DeviceService{{}}, nil}}), - addr: successNewName, - expectedError: true, - expectedErrorVal: errors.NewErrAddressableInUse(SuccessfulDatabaseResult[0].Name), - }, - { - name: "Unsuccessful database call retrieving addressable", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetAddressableById", SuccessfulDatabaseResult[0].Id, contract.Addressable{}, Error}}), - addr: SuccessfulDatabaseResult[0], - expectedError: true, - expectedErrorVal: Error, - }, - { - name: "Unsuccessful database call updating addressable", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetAddressableById", SuccessfulDatabaseResult[0].Id, SuccessfulDatabaseResult[0], nil}, - {"UpdateAddressable", mock.Anything, Error, nil}}), - addr: SuccessfulDatabaseResult[0], - expectedError: true, - expectedErrorVal: Error, - }, - { - name: "Addressable not found", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetAddressableById", SuccessfulDatabaseResult[0].Id, contract.Addressable{}, db.ErrNotFound}}), - addr: SuccessfulDatabaseResult[0], - expectedError: true, - expectedErrorVal: errors.NewErrAddressableNotFound(SuccessfulDatabaseResult[0].Id, SuccessfulDatabaseResult[0].Name), - }, - } - - for _, test := range tests { - t.Run(test.name, func(tt *testing.T) { - op := NewUpdateExecutor(test.mockUpdater, test.addr) - err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -type mockOutline struct { - methodName string - arg interface{} - ret interface{} - err error -} - -func createMockUpdater(outlines []mockOutline) AddressUpdater { - dbMock := mocks.AddressUpdater{} - - for _, o := range outlines { - dbMock.On(o.methodName, o.arg).Return(o.ret, o.err) - } - - return &dbMock -} diff --git a/internal/core/metadata/operators/command/db.go b/internal/core/metadata/operators/command/db.go deleted file mode 100644 index fc381b9bc1..0000000000 --- a/internal/core/metadata/operators/command/db.go +++ /dev/null @@ -1,12 +0,0 @@ -package command - -import ( - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type CommandLoader interface { - GetCommandsByDeviceId(did string) ([]contract.Command, error) - GetAllCommands() ([]contract.Command, error) - GetCommandById(id string) (contract.Command, error) - GetCommandsByName(id string) ([]contract.Command, error) -} diff --git a/internal/core/metadata/operators/command/get.go b/internal/core/metadata/operators/command/get.go deleted file mode 100644 index 53f435d405..0000000000 --- a/internal/core/metadata/operators/command/get.go +++ /dev/null @@ -1,111 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 command - -import ( - "fmt" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -//Get commands by device id -type DeviceIdExecutor interface { - Execute() ([]contract.Command, error) -} - -type commandLoadByDeviceId struct { - database CommandLoader - deviceId string -} - -func NewDeviceIdExecutor(db CommandLoader, deviceId string) DeviceIdExecutor { - return commandLoadByDeviceId{database: db, deviceId: deviceId} -} - -func (op commandLoadByDeviceId) Execute() (commands []contract.Command, err error) { - return op.database.GetCommandsByDeviceId(op.deviceId) -} - -//Get All commands -type CommandAllExecutor interface { - Execute() ([]contract.Command, error) -} - -type commandLoadAll struct { - config bootstrapConfig.ServiceInfo - database CommandLoader -} - -func NewCommandLoadAll(cfg bootstrapConfig.ServiceInfo, db CommandLoader) CommandAllExecutor { - return commandLoadAll{config: cfg, database: db} -} - -func (op commandLoadAll) Execute() (cmds []contract.Command, err error) { - cmds, err = op.database.GetAllCommands() - if err != nil { - return - } - - if len(cmds) > op.config.MaxResultCount { - err = errors.NewErrLimitExceeded(op.config.MaxResultCount) - return []contract.Command{}, err - } - return -} - -//Get Command By ID -type CommandByIdExecutor interface { - Execute() (contract.Command, error) -} - -type commandByIdLoader struct { - database CommandLoader - cid string -} - -func NewCommandById(db CommandLoader, cid string) CommandByIdExecutor { - return commandByIdLoader{database: db, cid: cid} -} - -func (op commandByIdLoader) Execute() (cmd contract.Command, err error) { - cmd, err = op.database.GetCommandById(op.cid) - if err != nil && err == db.ErrNotFound { - err = errors.NewErrItemNotFound(fmt.Sprintf("command with id %s not found", op.cid)) - } - return -} - -//Get Command By Name -type CommandsByNameExecutor interface { - Execute() ([]contract.Command, error) -} - -type commandsByNameLoader struct { - database CommandLoader - cname string -} - -func NewCommandsByName(db CommandLoader, cname string) CommandsByNameExecutor { - return commandsByNameLoader{database: db, cname: cname} -} - -func (op commandsByNameLoader) Execute() (cmd []contract.Command, err error) { - return op.database.GetCommandsByName(op.cname) -} diff --git a/internal/core/metadata/operators/command/get_test.go b/internal/core/metadata/operators/command/get_test.go deleted file mode 100644 index 7ee0708fa5..0000000000 --- a/internal/core/metadata/operators/command/get_test.go +++ /dev/null @@ -1,222 +0,0 @@ -package command - -import ( - "reflect" - "testing" - - mock "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/command/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/pkg/errors" -) - -func TestLoadCommandsByDeviceId(t *testing.T) { - tests := []struct { - name string - commandDBMock CommandLoader - expectedResult []models.Command - expectError bool - }{ - { - "GetCommandsByDeviceIdOK", - createCommandLoaderMock("GetCommandsByDeviceId", []models.Command{testCommand}, nil, newDeviceId), - []models.Command{testCommand}, - false, - }, - { - "GetCommandsByDeviceIdFailedNotFound", - createCommandLoaderMock("GetCommandsByDeviceId", nil, db.ErrNotFound, newDeviceId), - nil, - true, - }, - { - "GetCommandsByDeviceIdFailedUnexpected", - createCommandLoaderMock("GetCommandsByDeviceId", nil, errors.New("unexpected error"), newDeviceId), - nil, - true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - opp := NewDeviceIdExecutor(tt.commandDBMock, newDeviceId) - result, err := opp.Execute() - if tt.expectError && err == nil { - t.Error("Expected an error") - return - } - if !tt.expectError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(tt.expectedResult, result) { - t.Errorf("Expected result does not match the observed. \nExpected : %v \n Observed: %v", tt.expectError, result) - return - } - }) - } -} - -func TestLoadAllCommands(t *testing.T) { - tests := []struct { - name string - cfg bootstrapConfig.ServiceInfo - commandDBMock CommandLoader - expectedResult []models.Command - expectError bool - }{ - { - "GetAllOK", - bootstrapConfig.ServiceInfo{MaxResultCount: 1}, - createCommandLoaderMock("GetAllCommands", []models.Command{testCommand}, nil, ""), - []models.Command{testCommand}, - false, - }, - { - "GetAllFailMaxExceeded", - bootstrapConfig.ServiceInfo{}, - createCommandLoaderMock("GetAllCommands", []models.Command{testCommand}, nil, ""), - []models.Command{}, - true, - }, - { - "GetAllFailUnexpected", - bootstrapConfig.ServiceInfo{}, - createCommandLoaderMock("GetAllCommands", nil, errors.New("unexpected error"), ""), - nil, - true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - opp := NewCommandLoadAll(tt.cfg, tt.commandDBMock) - result, err := opp.Execute() - if tt.expectError && err == nil { - t.Error("Expected an error") - return - } - if !tt.expectError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - if !reflect.DeepEqual(tt.expectedResult, result) { - t.Errorf("Expected result does not match the observed. \nExpected : %v \n Observed: %v", tt.expectedResult, result) - return - } - }) - } -} - -func TestLoadCommandsById(t *testing.T) { - tests := []struct { - name string - commandDBMock CommandLoader - expectedResult models.Command - expectError bool - }{ - { - "GetCommandByIdOK", - createCommandLoaderMock("GetCommandById", testCommand, nil, commandId), - testCommand, - false, - }, - { - "GetCommandByIdFailNotFound", - createCommandLoaderMock("GetCommandById", models.Command{}, db.ErrNotFound, commandId), - models.Command{}, - true, - }, - { - "GetCommandByIdFailUnexpected", - createCommandLoaderMock("GetCommandById", models.Command{}, errors.New("unexpected error"), commandId), - models.Command{}, - true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - opp := NewCommandById(tt.commandDBMock, commandId) - result, err := opp.Execute() - if tt.expectError && err == nil { - t.Error("Expected an error") - return - } - if !tt.expectError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(tt.expectedResult, result) { - t.Errorf("Expected result does not match the observed. \nExpected : %v \n Observed: %v", tt.expectError, result) - return - } - - }) - } -} - -func TestLoadCommandsByName(t *testing.T) { - tests := []struct { - name string - commandDBMock CommandLoader - expectedResult []models.Command - expectError bool - }{ - { - "GetByNameOK", - createCommandLoaderMock("GetCommandsByName", []models.Command{testCommand}, nil, commandName), - []models.Command{testCommand}, - false, - }, - { - "GetByNameFailUnexpected", - createCommandLoaderMock("GetCommandsByName", nil, errors.New("unexpected error"), commandName), - nil, - true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - opp := NewCommandsByName(tt.commandDBMock, commandName) - result, err := opp.Execute() - if tt.expectError && err == nil { - t.Error("Expected an error") - return - } - if !tt.expectError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - if !reflect.DeepEqual(tt.expectedResult, result) { - t.Errorf("Expected result does not match the observed. \nExpected : %v \n Observed: %v", tt.expectedResult, result) - return - } - }) - } -} - -func createCommandLoaderMock(methodName string, ret interface{}, err error, arg string) CommandLoader { - dbCommandMock := &mock.CommandLoader{} - if arg != "" { - dbCommandMock.On(methodName, arg).Return(ret, err) - } else { - dbCommandMock.On(methodName).Return(ret, err) - } - return dbCommandMock -} - -var commandId = "f97b5f0a-ec32-4e96-bd36-02210af16f8c" -var commandName = "test command name" -var newDeviceId = "b3445cc6-87df-48f4-b8b0-587dc8a4e1c2" -var testCommand = models.Command{Timestamps: testTimestamps, Name: commandName, Get: models.Get{Action: testAction}, - Put: models.Put{Action: testAction, ParameterNames: testExpectedvalues}} - -var testExpectedvalues = []string{"temperature", "humidity"} -var testAction = models.Action{Path: "test/path", Responses: []models.Response{{Code: "200", Description: "ok", ExpectedValues: testExpectedvalues}}, URL: ""} - -var testTimestamps = models.Timestamps{Created: 123, Modified: 123, Origin: 123} -var testDescribedObject = models.DescribedObject{Timestamps: testTimestamps, Description: "This is a description"} diff --git a/internal/core/metadata/operators/command/mocks/CommandLoader.go b/internal/core/metadata/operators/command/mocks/CommandLoader.go deleted file mode 100644 index 7ad8267cd1..0000000000 --- a/internal/core/metadata/operators/command/mocks/CommandLoader.go +++ /dev/null @@ -1,101 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// CommandLoader is an autogenerated mock type for the CommandLoader type -type CommandLoader struct { - mock.Mock -} - -// GetAllCommands provides a mock function with given fields: -func (_m *CommandLoader) GetAllCommands() ([]models.Command, error) { - ret := _m.Called() - - var r0 []models.Command - if rf, ok := ret.Get(0).(func() []models.Command); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Command) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetCommandById provides a mock function with given fields: id -func (_m *CommandLoader) GetCommandById(id string) (models.Command, error) { - ret := _m.Called(id) - - var r0 models.Command - if rf, ok := ret.Get(0).(func(string) models.Command); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Command) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetCommandsByDeviceId provides a mock function with given fields: did -func (_m *CommandLoader) GetCommandsByDeviceId(did string) ([]models.Command, error) { - ret := _m.Called(did) - - var r0 []models.Command - if rf, ok := ret.Get(0).(func(string) []models.Command); ok { - r0 = rf(did) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Command) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(did) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetCommandsByName provides a mock function with given fields: id -func (_m *CommandLoader) GetCommandsByName(id string) ([]models.Command, error) { - ret := _m.Called(id) - - var r0 []models.Command - if rf, ok := ret.Get(0).(func(string) []models.Command); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Command) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/metadata/operators/device/add.go b/internal/core/metadata/operators/device/add.go deleted file mode 100644 index ebf91d1962..0000000000 --- a/internal/core/metadata/operators/device/add.go +++ /dev/null @@ -1,96 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device - -import ( - "fmt" - "net/http" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type DeviceCreator interface { - Execute() (id string, err error) -} - -type addDevice struct { - database DeviceAdder - device contract.Device - events chan DeviceEvent -} - -func NewAddDevice(ch chan DeviceEvent, db DeviceAdder, new contract.Device) DeviceCreator { - return addDevice{database: db, device: new, events: ch} -} - -func (op addDevice) Execute() (id string, err error) { - evt := DeviceEvent{} - // Lookup device service by name, then ID. Verify it exists. - // ** TODO: Change this to CheckDeviceServiceByName ** - service, err := op.database.GetDeviceServiceByName(op.device.Service.Name) - if err != nil { - // Try by ID - service, err = op.database.GetDeviceServiceById(op.device.Service.Id) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrItemNotFound(fmt.Sprintf("invalid device service: %s %s", op.device.Service.Name, op.device.Service.Id)) - } - evt.Error = err - op.events <- evt - return - } - } - // TODO: Can decouple DeviceService (see "check" above) - op.device.Service = service - - // Lookup device profile by name, then ID. Verify it exists. - profile, err := op.database.GetDeviceProfileByName(op.device.Profile.Name) - if err != nil { - // Try by ID - profile, err = op.database.GetDeviceProfileById(op.device.Profile.Id) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrItemNotFound(fmt.Sprintf("invalid device profile: %s %s", op.device.Profile.Name, op.device.Profile.Id)) - } - evt.Error = err - op.events <- evt - return - } - } - // TODO: Will be decoupling the DeviceProfile - op.device.Profile = profile - - // Add the device - id, err = op.database.AddDevice(op.device, profile.CoreCommands) - if err != nil { - if err == db.ErrNotUnique { - err = errors.NewErrDuplicateName(fmt.Sprintf("duplicate name for device: %s", op.device.Name)) - } - evt.Error = err - op.events <- evt - return - } - - //Device added successfully. Publish event onto the supplied channel. - evt.DeviceId = id - evt.DeviceName = op.device.Name - evt.HttpMethod = http.MethodPost - evt.ServiceId = service.Id - op.events <- evt - - return id, nil -} diff --git a/internal/core/metadata/operators/device/add_test.go b/internal/core/metadata/operators/device/add_test.go deleted file mode 100644 index c459030df5..0000000000 --- a/internal/core/metadata/operators/device/add_test.go +++ /dev/null @@ -1,194 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device - -import ( - "sync" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/device/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/google/uuid" -) - -func TestAddNewDevice(t *testing.T) { - tests := []struct { - name string - dbMock DeviceAdder - expectError bool - }{ - {"AddDeviceCheckByName", createAddDeviceDbMockForName(), false}, - {"AddDeviceCheckById", createAddDeviceDbMockForId(), false}, - {"AddDeviceFailNoDeviceService", createAddDeviceDbMockFailDeviceService(), true}, - {"AddDeviceFailNoDeviceProfile", createAddDeviceDbMockFailDeviceProfile(), true}, - {"AddDeviceFailDuplicateName", createAddDeviceDbMockDuplicateName(), true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var wg sync.WaitGroup - - ch := make(chan DeviceEvent) - defer close(ch) - - wg.Add(1) - go func(wg *sync.WaitGroup, t *testing.T) { - defer wg.Done() - msg, ok := <-ch - if !ok { - t.Errorf("%s unsuccessful read from channel", t.Name()) - return - } - if msg.Error != nil && !tt.expectError { - t.Errorf("%s error reported via channel: %s", t.Name(), msg.Error.Error()) - return - } - }(&wg, t) - - op := NewAddDevice(ch, tt.dbMock, testDevice) - newId, err := op.Execute() - // Unexpected error, something weird happened - if err != nil && !tt.expectError { - t.Error(err) - return - } - _, err = uuid.Parse(newId) - // Returned value doesn't parse to a UUID - if err != nil && !tt.expectError { - t.Error(err) - return - } - wg.Wait() - }) - } -} - -func createAddDeviceDbMockForName() DeviceAdder { - dbMock := &mocks.DeviceAdder{} - dbMock.On("AddDevice", testDevice, testDeviceProfile.CoreCommands).Return(uuid.New().String(), nil) - dbMock.On("GetDeviceProfileByName", testDeviceProfileName).Return(testDeviceProfile, nil) - dbMock.On("GetDeviceServiceByName", testDeviceServiceName).Return(testDeviceService, nil) - return dbMock -} - -func createAddDeviceDbMockForId() DeviceAdder { - dbMock := &mocks.DeviceAdder{} - dbMock.On("AddDevice", testDevice, testDeviceProfile.CoreCommands).Return(uuid.New().String(), nil) - dbMock.On("GetDeviceProfileById", testDeviceProfileId).Return(testDeviceProfile, nil) - dbMock.On("GetDeviceProfileByName", testDeviceProfileName).Return(models.DeviceProfile{}, db.ErrNotFound) - dbMock.On("GetDeviceServiceById", testDeviceServiceId).Return(testDeviceService, nil) - dbMock.On("GetDeviceServiceByName", testDeviceServiceName).Return(models.DeviceService{}, db.ErrNotFound) - return dbMock -} - -func createAddDeviceDbMockFailDeviceService() DeviceAdder { - dbMock := &mocks.DeviceAdder{} - dbMock.On("AddDevice", testDevice, testDeviceProfile.CoreCommands).Return(uuid.New().String(), nil) - dbMock.On("GetDeviceServiceById", testDeviceServiceId).Return(models.DeviceService{}, db.ErrNotFound) - dbMock.On("GetDeviceServiceByName", testDeviceServiceName).Return(models.DeviceService{}, db.ErrNotFound) - return dbMock -} - -func createAddDeviceDbMockFailDeviceProfile() DeviceAdder { - dbMock := &mocks.DeviceAdder{} - dbMock.On("AddDevice", testDevice, testDeviceProfile.CoreCommands).Return(uuid.New().String(), nil) - dbMock.On("GetDeviceServiceByName", testDeviceServiceName).Return(testDeviceService, nil) - dbMock.On("GetDeviceProfileById", testDeviceProfileId).Return(models.DeviceProfile{}, db.ErrNotFound) - dbMock.On("GetDeviceProfileByName", testDeviceProfileName).Return(models.DeviceProfile{}, db.ErrNotFound) - return dbMock -} - -func createAddDeviceDbMockDuplicateName() DeviceAdder { - dbMock := &mocks.DeviceAdder{} - dbMock.On("AddDevice", testDevice, testDeviceProfile.CoreCommands).Return("", db.ErrNotUnique) - dbMock.On("GetDeviceProfileByName", testDeviceProfileName).Return(testDeviceProfile, nil) - dbMock.On("GetDeviceServiceByName", testDeviceServiceName).Return(testDeviceService, nil) - return dbMock -} - -// Device Profile mock instance setup -var testExpectedvalues = []string{"temperature", "humidity"} -var testAction = models.Action{Path: "test/path", Responses: []models.Response{{Code: "200", Description: "ok", ExpectedValues: testExpectedvalues}}, URL: ""} - -var testTimestamps = models.Timestamps{Created: 123, Modified: 123, Origin: 123} -var testDescribedObject = models.DescribedObject{Timestamps: testTimestamps, Description: "This is a description"} - -var testUnits = models.Units{Type: "String", ReadWrite: "R", DefaultValue: "Degrees Fahrenheit"} -var testPropertyValue = models.PropertyValue{Type: "Float", ReadWrite: "RW", Minimum: "-99.99", Maximum: "199.99", - DefaultValue: "0.00", Size: "8", Mask: "0x00", Shift: "0", Scale: "1.0", Offset: "0.0", Base: "0", - Assertion: "0", Precision: "1", FloatEncoding: models.Base64Encoding, MediaType: clients.ContentTypeJSON} -var testProfileProperty = models.ProfileProperty{Value: testPropertyValue, Units: testUnits} -var testDeviceResource = models.DeviceResource{Description: "test device object description", - Name: "test device object name", Tag: "test device object tag", Properties: testProfileProperty} - -var testResourceOperation = models.ResourceOperation{Index: "test index", Operation: "test operation", Object: "test resource object", - Parameter: "test parameter", Resource: "test resource", Secondary: []string{"test secondary"}, Mappings: make(map[string]string)} -var testProfileResource = models.ProfileResource{Name: "test profile resource name", Get: []models.ResourceOperation{testResourceOperation}, Set: []models.ResourceOperation{testResourceOperation}} - -var testCommand = models.Command{Timestamps: testTimestamps, Name: "test command name", Get: models.Get{Action: testAction}, - Put: models.Put{Action: testAction, ParameterNames: testExpectedvalues}} - -var testDeviceProfileId = uuid.New().String() -var testDeviceProfileName = "Test Profile.NAME" -var testDeviceProfile = models.DeviceProfile{Id: testDeviceProfileId, DescribedObject: testDescribedObject, Name: testDeviceProfileName, - Manufacturer: "Test Manufacturer", Model: "Test Model", Labels: []string{"labe1", "label2"}, - DeviceResources: []models.DeviceResource{testDeviceResource}, DeviceCommands: []models.ProfileResource{testProfileResource}, - CoreCommands: []models.Command{testCommand}} - -// Device Service mock instance setup -var testAddressable = models.Addressable{Timestamps: testTimestamps, Name: "TEST_ADDR.NAME", Protocol: "HTTP", HTTPMethod: "Get", - Address: "localhost", Port: 48089, Path: clients.ApiDeviceRoute, Publisher: "TEST_PUB", User: "edgexer", Password: "password", - Topic: "device_topic"} - -var testDeviceServiceId = uuid.New().String() -var testDeviceServiceName = "test service" -var testDeviceService = models.DeviceService{Id: testDeviceServiceId, DescribedObject: testDescribedObject, Name: testDeviceServiceName, - LastConnected: int64(1000000), LastReported: int64(1000000), OperatingState: "ENABLED", Labels: []string{"MODBUS", "TEMP"}, - Addressable: testAddressable, AdminState: "UNLOCKED"} - -// Device mock instance setup -var testDeviceName = "test device name" -var testDevice = models.Device{DescribedObject: testDescribedObject, Name: testDeviceName, AdminState: "UNLOCKED", - OperatingState: "ENABLED", Protocols: newTestProtocols(), LastReported: int64(1000000), LastConnected: int64(1000000), - Labels: []string{"MODBUS", "TEMP"}, Location: "{40lat;45long}", Service: testDeviceService, Profile: testDeviceProfile, - AutoEvents: newAutoEvent()} - -func newAutoEvent() []models.AutoEvent { - a := []models.AutoEvent{} - a = append(a, models.AutoEvent{Resource: "TestDevice", Frequency: "300ms", OnChange: true}) - return a -} - -func newTestProtocols() map[string]models.ProtocolProperties { - p1 := make(models.ProtocolProperties) - p1["host"] = "localhost" - p1["port"] = "1234" - p1["unitID"] = "1" - - p2 := make(models.ProtocolProperties) - p2["serialPort"] = "/dev/USB0" - p2["baudRate"] = "19200" - p2["dataBits"] = "8" - p2["stopBits"] = "1" - p2["parity"] = "0" - p2["unitID"] = "2" - - wrap := make(map[string]models.ProtocolProperties) - wrap["modbus-ip"] = p1 - wrap["modbus-rtu"] = p2 - - return wrap -} diff --git a/internal/core/metadata/operators/device/db.go b/internal/core/metadata/operators/device/db.go deleted file mode 100644 index 8741842d0f..0000000000 --- a/internal/core/metadata/operators/device/db.go +++ /dev/null @@ -1,34 +0,0 @@ -package device - -import ( - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type DeviceAdder interface { - AddDevice(d contract.Device, commands []contract.Command) (string, error) - DeviceProfileLoader - DeviceServiceLoader -} - -type DeviceServiceLoader interface { - GetDeviceServiceById(id string) (contract.DeviceService, error) - GetDeviceServiceByName(n string) (contract.DeviceService, error) -} - -type DeviceLoader interface { - GetAllDevices() ([]contract.Device, error) - GetDevicesByProfileId(pid string) ([]contract.Device, error) -} - -type DeviceUpdater interface { - UpdateDevice(d contract.Device) error - GetDeviceById(id string) (contract.Device, error) - GetDeviceByName(name string) (contract.Device, error) - DeviceProfileLoader - DeviceServiceLoader -} - -type DeviceProfileLoader interface { - GetDeviceProfileById(id string) (contract.DeviceProfile, error) - GetDeviceProfileByName(n string) (contract.DeviceProfile, error) -} diff --git a/internal/core/metadata/operators/device/events.go b/internal/core/metadata/operators/device/events.go deleted file mode 100644 index 6fcc6b418e..0000000000 --- a/internal/core/metadata/operators/device/events.go +++ /dev/null @@ -1,10 +0,0 @@ -package device - -type DeviceEvent struct { - DeviceId string - DeviceName string - Error error - HttpMethod string - ServiceId string - SecondaryServiceId string -} diff --git a/internal/core/metadata/operators/device/get.go b/internal/core/metadata/operators/device/get.go deleted file mode 100644 index f9436e2732..0000000000 --- a/internal/core/metadata/operators/device/get.go +++ /dev/null @@ -1,85 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device - -import ( - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type DeviceAllExecutor interface { - Execute() ([]contract.Device, error) -} - -type deviceLoadAll struct { - config bootstrapConfig.ServiceInfo - database DeviceLoader - logger logger.LoggingClient -} - -func NewDeviceLoadAll(cfg bootstrapConfig.ServiceInfo, db DeviceLoader, log logger.LoggingClient) DeviceAllExecutor { - return deviceLoadAll{config: cfg, database: db, logger: log} -} - -func (op deviceLoadAll) Execute() (devices []contract.Device, err error) { - devices, err = op.database.GetAllDevices() - if err != nil { - op.logger.Error(err.Error()) - return - } - - if len(devices) > op.config.MaxResultCount { - err = errors.NewErrLimitExceeded(op.config.MaxResultCount) - return []contract.Device{}, err - } - return -} - -// ProfileIdExecutor provides functionality for loading devices by way of the operator pattern. -type ProfileIdExecutor interface { - Execute() ([]contract.Device, error) -} - -type deviceLoadByProfileId struct { - config bootstrapConfig.ServiceInfo - database DeviceLoader - profile string - logger logger.LoggingClient -} - -// NewProfileIdExecutor creates a new ProfileIdExecutor -func NewProfileIdExecutor(cfg bootstrapConfig.ServiceInfo, db DeviceLoader, log logger.LoggingClient, profile string) ProfileIdExecutor { - return deviceLoadByProfileId{config: cfg, database: db, logger: log, profile: profile} -} - -// Execute retrieves the devices associated with a profile ID -func (op deviceLoadByProfileId) Execute() (devices []contract.Device, err error) { - devices, err = op.database.GetDevicesByProfileId(op.profile) - if err != nil { - op.logger.Error(err.Error()) - return - } - - if len(devices) > op.config.MaxResultCount { - err = errors.NewErrLimitExceeded(op.config.MaxResultCount) - return []contract.Device{}, err - } - - return -} diff --git a/internal/core/metadata/operators/device/get_test.go b/internal/core/metadata/operators/device/get_test.go deleted file mode 100644 index 8286abb6f8..0000000000 --- a/internal/core/metadata/operators/device/get_test.go +++ /dev/null @@ -1,114 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device - -import ( - "github.com/pkg/errors" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/device/mocks" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -var TestDeviceProfileID = "TestDeviceProfileID" - -func TestGetAllDevices(t *testing.T) { - tests := []struct { - name string - cfg bootstrapConfig.ServiceInfo - dbMock DeviceLoader - expectError bool - }{ - {"GetAllPass", bootstrapConfig.ServiceInfo{MaxResultCount: 1}, createDeviceLoaderMockNoArg("GetAllDevices"), false}, - {"GetAllFailCount", bootstrapConfig.ServiceInfo{}, createDeviceLoaderMockNoArg("GetAllDevices"), true}, - {"GetAllFailUnexpected", bootstrapConfig.ServiceInfo{MaxResultCount: 1}, createGetDeviceLoaderMockFail("GetAllDevices"), true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - op := NewDeviceLoadAll(tt.cfg, tt.dbMock, logger.NewMockClient()) - _, err := op.Execute() - if err != nil && !tt.expectError { - t.Error(err) - return - } - - if err == nil && tt.expectError { - t.Errorf("error was expected, none occurred") - - return - } - }) - } -} - -func TestGetDeviceByProfileId(t *testing.T) { - tests := []struct { - name string - cfg bootstrapConfig.ServiceInfo - dbMock DeviceLoader - expectError bool - }{ - {"Get devices by Profile ID", bootstrapConfig.ServiceInfo{MaxResultCount: 1}, createDeviceLoaderMockStringArg("GetDevicesByProfileId"), false}, - {"Get devices more than maximum", bootstrapConfig.ServiceInfo{}, createDeviceLoaderMockStringArg("GetDevicesByProfileId"), true}, - {"Get devices fail", bootstrapConfig.ServiceInfo{MaxResultCount: 1}, createDeviceLoaderMockStringArgFail("GetDevicesByProfileId"), true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - op := NewProfileIdExecutor(tt.cfg, tt.dbMock, logger.MockLogger{}, TestDeviceProfileID) - _, err := op.Execute() - if err != nil && !tt.expectError { - t.Error(err) - - return - } - - if err == nil && tt.expectError { - t.Errorf("error was expected, none occurred") - - return - } - }) - } -} - -func createDeviceLoaderMockNoArg(methodName string) DeviceLoader { - devices := []contract.Device{testDevice} - dbMock := &mocks.DeviceLoader{} - dbMock.On(methodName).Return(devices, nil) - return dbMock -} - -func createGetDeviceLoaderMockFail(methodName string) DeviceLoader { - dbMock := &mocks.DeviceLoader{} - dbMock.On(methodName).Return(nil, errors.New("unexpected error")) - return dbMock -} - -func createDeviceLoaderMockStringArg(methodName string) DeviceLoader { - devices := []contract.Device{testDevice} - dbMock := &mocks.DeviceLoader{} - dbMock.On(methodName, TestDeviceProfileID).Return(devices, nil) - return dbMock -} - -func createDeviceLoaderMockStringArgFail(methodName string) DeviceLoader { - dbMock := &mocks.DeviceLoader{} - dbMock.On(methodName, TestDeviceProfileID).Return(nil, errors.New("unexpected error")) - return dbMock -} diff --git a/internal/core/metadata/operators/device/mocks/DeviceAdder.go b/internal/core/metadata/operators/device/mocks/DeviceAdder.go deleted file mode 100644 index 14740aa8b8..0000000000 --- a/internal/core/metadata/operators/device/mocks/DeviceAdder.go +++ /dev/null @@ -1,116 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// DeviceAdder is an autogenerated mock type for the DeviceAdder type -type DeviceAdder struct { - mock.Mock -} - -// AddDevice provides a mock function with given fields: d, commands -func (_m *DeviceAdder) AddDevice(d models.Device, commands []models.Command) (string, error) { - ret := _m.Called(d, commands) - - var r0 string - if rf, ok := ret.Get(0).(func(models.Device, []models.Command) string); ok { - r0 = rf(d, commands) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.Device, []models.Command) error); ok { - r1 = rf(d, commands) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfileById provides a mock function with given fields: id -func (_m *DeviceAdder) GetDeviceProfileById(id string) (models.DeviceProfile, error) { - ret := _m.Called(id) - - var r0 models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) models.DeviceProfile); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.DeviceProfile) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfileByName provides a mock function with given fields: n -func (_m *DeviceAdder) GetDeviceProfileByName(n string) (models.DeviceProfile, error) { - ret := _m.Called(n) - - var r0 models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) models.DeviceProfile); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.DeviceProfile) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceServiceById provides a mock function with given fields: id -func (_m *DeviceAdder) GetDeviceServiceById(id string) (models.DeviceService, error) { - ret := _m.Called(id) - - var r0 models.DeviceService - if rf, ok := ret.Get(0).(func(string) models.DeviceService); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.DeviceService) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceServiceByName provides a mock function with given fields: n -func (_m *DeviceAdder) GetDeviceServiceByName(n string) (models.DeviceService, error) { - ret := _m.Called(n) - - var r0 models.DeviceService - if rf, ok := ret.Get(0).(func(string) models.DeviceService); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.DeviceService) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/metadata/operators/device/mocks/DeviceLoader.go b/internal/core/metadata/operators/device/mocks/DeviceLoader.go deleted file mode 100644 index 0eb6dd18eb..0000000000 --- a/internal/core/metadata/operators/device/mocks/DeviceLoader.go +++ /dev/null @@ -1,57 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// DeviceLoader is an autogenerated mock type for the DeviceLoader type -type DeviceLoader struct { - mock.Mock -} - -// GetAllDevices provides a mock function with given fields: -func (_m *DeviceLoader) GetAllDevices() ([]models.Device, error) { - ret := _m.Called() - - var r0 []models.Device - if rf, ok := ret.Get(0).(func() []models.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Device) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDevicesByProfileId provides a mock function with given fields: pid -func (_m *DeviceLoader) GetDevicesByProfileId(pid string) ([]models.Device, error) { - ret := _m.Called(pid) - - var r0 []models.Device - if rf, ok := ret.Get(0).(func(string) []models.Device); ok { - r0 = rf(pid) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Device) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(pid) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/metadata/operators/device/mocks/DeviceProfileDeleter.go b/internal/core/metadata/operators/device/mocks/DeviceProfileDeleter.go deleted file mode 100644 index 4d1fc8b01d..0000000000 --- a/internal/core/metadata/operators/device/mocks/DeviceProfileDeleter.go +++ /dev/null @@ -1,136 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// DeviceProfileDeleter is an autogenerated mock type for the DeviceProfileDeleter type -type DeviceProfileDeleter struct { - mock.Mock -} - -// DeleteDeviceProfileById provides a mock function with given fields: id -func (_m *DeviceProfileDeleter) DeleteDeviceProfileById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// GetAllDevices provides a mock function with given fields: -func (_m *DeviceProfileDeleter) GetAllDevices() ([]models.Device, error) { - ret := _m.Called() - - var r0 []models.Device - if rf, ok := ret.Get(0).(func() []models.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Device) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfileById provides a mock function with given fields: id -func (_m *DeviceProfileDeleter) GetDeviceProfileById(id string) (models.DeviceProfile, error) { - ret := _m.Called(id) - - var r0 models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) models.DeviceProfile); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.DeviceProfile) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfileByName provides a mock function with given fields: n -func (_m *DeviceProfileDeleter) GetDeviceProfileByName(n string) (models.DeviceProfile, error) { - ret := _m.Called(n) - - var r0 models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) models.DeviceProfile); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.DeviceProfile) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDevicesByProfileId provides a mock function with given fields: pid -func (_m *DeviceProfileDeleter) GetDevicesByProfileId(pid string) ([]models.Device, error) { - ret := _m.Called(pid) - - var r0 []models.Device - if rf, ok := ret.Get(0).(func(string) []models.Device); ok { - r0 = rf(pid) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Device) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(pid) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetProvisionWatchersByProfileId provides a mock function with given fields: id -func (_m *DeviceProfileDeleter) GetProvisionWatchersByProfileId(id string) ([]models.ProvisionWatcher, error) { - ret := _m.Called(id) - - var r0 []models.ProvisionWatcher - if rf, ok := ret.Get(0).(func(string) []models.ProvisionWatcher); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.ProvisionWatcher) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/metadata/operators/device/mocks/DeviceProfileUpdater.go b/internal/core/metadata/operators/device/mocks/DeviceProfileUpdater.go deleted file mode 100644 index 64b68a443a..0000000000 --- a/internal/core/metadata/operators/device/mocks/DeviceProfileUpdater.go +++ /dev/null @@ -1,159 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// DeviceProfileUpdater is an autogenerated mock type for the DeviceProfileUpdater type -type DeviceProfileUpdater struct { - mock.Mock -} - -// GetAllDeviceProfiles provides a mock function with given fields: -func (_m *DeviceProfileUpdater) GetAllDeviceProfiles() ([]models.DeviceProfile, error) { - ret := _m.Called() - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func() []models.DeviceProfile); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAllDevices provides a mock function with given fields: -func (_m *DeviceProfileUpdater) GetAllDevices() ([]models.Device, error) { - ret := _m.Called() - - var r0 []models.Device - if rf, ok := ret.Get(0).(func() []models.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Device) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfileById provides a mock function with given fields: id -func (_m *DeviceProfileUpdater) GetDeviceProfileById(id string) (models.DeviceProfile, error) { - ret := _m.Called(id) - - var r0 models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) models.DeviceProfile); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.DeviceProfile) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfileByName provides a mock function with given fields: n -func (_m *DeviceProfileUpdater) GetDeviceProfileByName(n string) (models.DeviceProfile, error) { - ret := _m.Called(n) - - var r0 models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) models.DeviceProfile); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.DeviceProfile) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDevicesByProfileId provides a mock function with given fields: pid -func (_m *DeviceProfileUpdater) GetDevicesByProfileId(pid string) ([]models.Device, error) { - ret := _m.Called(pid) - - var r0 []models.Device - if rf, ok := ret.Get(0).(func(string) []models.Device); ok { - r0 = rf(pid) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Device) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(pid) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetProvisionWatchersByProfileId provides a mock function with given fields: id -func (_m *DeviceProfileUpdater) GetProvisionWatchersByProfileId(id string) ([]models.ProvisionWatcher, error) { - ret := _m.Called(id) - - var r0 []models.ProvisionWatcher - if rf, ok := ret.Get(0).(func(string) []models.ProvisionWatcher); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.ProvisionWatcher) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateDeviceProfile provides a mock function with given fields: dp -func (_m *DeviceProfileUpdater) UpdateDeviceProfile(dp models.DeviceProfile) error { - ret := _m.Called(dp) - - var r0 error - if rf, ok := ret.Get(0).(func(models.DeviceProfile) error); ok { - r0 = rf(dp) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/internal/core/metadata/operators/device/mocks/DeviceServiceLoader.go b/internal/core/metadata/operators/device/mocks/DeviceServiceLoader.go deleted file mode 100644 index a8d3489bf4..0000000000 --- a/internal/core/metadata/operators/device/mocks/DeviceServiceLoader.go +++ /dev/null @@ -1,53 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// DeviceServiceLoader is an autogenerated mock type for the DeviceServiceLoader type -type DeviceServiceLoader struct { - mock.Mock -} - -// GetDeviceServiceById provides a mock function with given fields: id -func (_m *DeviceServiceLoader) GetDeviceServiceById(id string) (models.DeviceService, error) { - ret := _m.Called(id) - - var r0 models.DeviceService - if rf, ok := ret.Get(0).(func(string) models.DeviceService); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.DeviceService) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceServiceByName provides a mock function with given fields: n -func (_m *DeviceServiceLoader) GetDeviceServiceByName(n string) (models.DeviceService, error) { - ret := _m.Called(n) - - var r0 models.DeviceService - if rf, ok := ret.Get(0).(func(string) models.DeviceService); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.DeviceService) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/metadata/operators/device/mocks/DeviceUpdater.go b/internal/core/metadata/operators/device/mocks/DeviceUpdater.go deleted file mode 100644 index e8991a0c27..0000000000 --- a/internal/core/metadata/operators/device/mocks/DeviceUpdater.go +++ /dev/null @@ -1,151 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// DeviceUpdater is an autogenerated mock type for the DeviceUpdater type -type DeviceUpdater struct { - mock.Mock -} - -// GetDeviceById provides a mock function with given fields: id -func (_m *DeviceUpdater) GetDeviceById(id string) (models.Device, error) { - ret := _m.Called(id) - - var r0 models.Device - if rf, ok := ret.Get(0).(func(string) models.Device); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Device) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceByName provides a mock function with given fields: name -func (_m *DeviceUpdater) GetDeviceByName(name string) (models.Device, error) { - ret := _m.Called(name) - - var r0 models.Device - if rf, ok := ret.Get(0).(func(string) models.Device); ok { - r0 = rf(name) - } else { - r0 = ret.Get(0).(models.Device) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfileById provides a mock function with given fields: id -func (_m *DeviceUpdater) GetDeviceProfileById(id string) (models.DeviceProfile, error) { - ret := _m.Called(id) - - var r0 models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) models.DeviceProfile); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.DeviceProfile) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfileByName provides a mock function with given fields: n -func (_m *DeviceUpdater) GetDeviceProfileByName(n string) (models.DeviceProfile, error) { - ret := _m.Called(n) - - var r0 models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) models.DeviceProfile); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.DeviceProfile) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceServiceById provides a mock function with given fields: id -func (_m *DeviceUpdater) GetDeviceServiceById(id string) (models.DeviceService, error) { - ret := _m.Called(id) - - var r0 models.DeviceService - if rf, ok := ret.Get(0).(func(string) models.DeviceService); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.DeviceService) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceServiceByName provides a mock function with given fields: n -func (_m *DeviceUpdater) GetDeviceServiceByName(n string) (models.DeviceService, error) { - ret := _m.Called(n) - - var r0 models.DeviceService - if rf, ok := ret.Get(0).(func(string) models.DeviceService); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.DeviceService) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateDevice provides a mock function with given fields: d -func (_m *DeviceUpdater) UpdateDevice(d models.Device) error { - ret := _m.Called(d) - - var r0 error - if rf, ok := ret.Get(0).(func(models.Device) error); ok { - r0 = rf(d) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/internal/core/metadata/operators/device/notify.go b/internal/core/metadata/operators/device/notify.go deleted file mode 100644 index fb0a095b6a..0000000000 --- a/internal/core/metadata/operators/device/notify.go +++ /dev/null @@ -1,145 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net/http" - "strconv" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/notifications" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - metaConfig "github.com/edgexfoundry/edgex-go/internal/core/metadata/config" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" -) - -type Notifier interface { - // Execute performs the main logic of the Notifier implementation. - // This function is meant to be called from a goroutine, thus log eny errors and do not return them. - Execute() -} - -type deviceNotifier struct { - ctx context.Context - database DeviceServiceLoader - events chan DeviceEvent - logger logger.LoggingClient - notifyClient notifications.NotificationsClient - notifyConfig metaConfig.NotificationInfo - requester Requester -} - -func NewNotifier(evt chan DeviceEvent, nc notifications.NotificationsClient, cfg metaConfig.NotificationInfo, - db DeviceServiceLoader, requester Requester, logger logger.LoggingClient, ctx context.Context) Notifier { - return deviceNotifier{ - ctx: ctx, - database: db, - events: evt, - logger: logger, - notifyClient: nc, - notifyConfig: cfg, - requester: requester, - } -} - -// Remember that this method is being invoked via a goroutine. The following logic is all async to the caller. -func (op deviceNotifier) Execute() { - select { - case msg := <-op.events: - if msg.Error != nil { - op.logger.Error(fmt.Sprintf("dropping event due to error: %s", msg.Error.Error())) - return // Something happened during the upstream operation. Do nothing. - } - deviceId := msg.DeviceId - deviceName := msg.DeviceName - httpMethod := msg.HttpMethod - - // Perform logic previously found in rest_device.go:: func notifyDeviceAssociates() - op.postNotification(deviceName, httpMethod) - - // Callback for device service - service, err := op.database.GetDeviceServiceById(msg.ServiceId) - if err != nil { - op.logger.Error(err.Error()) - return - } - - if err := op.callback(service, deviceId, httpMethod, models.DEVICE); err != nil { - op.logger.Error(err.Error()) - } - - if msg.SecondaryServiceId != "" { - // Callback for secondary device service - service, err := op.database.GetDeviceServiceById(msg.SecondaryServiceId) - if err != nil { - op.logger.Error(err.Error()) - return - } - - if err := op.callback(service, deviceId, httpMethod, models.DEVICE); err != nil { - op.logger.Error(err.Error()) - } - } - } -} - -func (op deviceNotifier) postNotification(name string, action string) { - // Only post notification if the configuration is set - if op.notifyConfig.PostDeviceChanges { - // Make the notification - notification := notifications.Notification{ - Slug: op.notifyConfig.Slug + strconv.FormatInt(db.MakeTimestamp(), 10), - Content: op.notifyConfig.Content + name + "-" + action, - Category: notifications.SW_HEALTH, - Description: op.notifyConfig.Description, - Labels: []string{op.notifyConfig.Label}, - Sender: op.notifyConfig.Sender, - Severity: notifications.NORMAL, - } - - _ = op.notifyClient.SendNotification(op.ctx, notification) - } -} - -// Make the callback for the device service -func (op deviceNotifier) callback( - service models.DeviceService, - id string, - action string, - actionType models.ActionType) error { - - url := service.Addressable.GetCallbackURL() - if len(url) > 0 { - body, err := json.Marshal(models.CallbackAlert{ActionType: actionType, Id: id}) - if err != nil { - return err - } - req, err := http.NewRequest(action, url, bytes.NewReader(body)) - if err != nil { - return err - } - req.Header.Add(clients.ContentType, clients.ContentTypeJSON) - go op.requester.Execute(req) - } else { - op.logger.Error("callback::no addressable for " + service.Name) - } - return nil -} diff --git a/internal/core/metadata/operators/device/notify_test.go b/internal/core/metadata/operators/device/notify_test.go deleted file mode 100644 index d649575de3..0000000000 --- a/internal/core/metadata/operators/device/notify_test.go +++ /dev/null @@ -1,194 +0,0 @@ -package device - -import ( - "context" - "net/http" - "sync" - "testing" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/notifications" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/google/uuid" - - metaConfig "github.com/edgexfoundry/edgex-go/internal/core/metadata/config" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/device/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" -) - -func TestNotification(t *testing.T) { - pass := DeviceEvent{ - DeviceId: uuid.New().String(), - DeviceName: testDeviceName, - HttpMethod: http.MethodPost, - ServiceId: testDeviceServiceId, - } - fail := DeviceEvent{Error: db.ErrNotFound} - failInvalidMethod := pass - failInvalidMethod.HttpMethod = "{}" - - tests := []struct { - name string - dbMock DeviceServiceLoader - event DeviceEvent - expectError bool - }{ - {"DeviceAdded", createNotifyDeviceServiceDbMock(), pass, false}, - {"ErrorOccurred", createNotifyDeviceServiceDbMock(), fail, true}, - {"NoDeviceService", createNotifyDeviceServiceDbMockFail(), pass, true}, - {"NoAddressable", createNotifyEmptyAddressableDbMockFail(), pass, true}, - {"InvalidRequest", createNotifyDeviceServiceDbMock(), failInvalidMethod, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var wg sync.WaitGroup - nc := mockNotificationClientOK{} - requester, err := NewRequester(Mock, logger.MockLogger{}, context.Background()) - if err != nil { - t.Error(err.Error()) - return - } - - ch := make(chan DeviceEvent) - defer close(ch) - - wg.Add(1) - op := NewNotifier( - ch, - nc, - testNotificationInfo, - tt.dbMock, - requester, - newMockNotifyLogger(tt.expectError, t), - context.Background(), - ) - go func(wg *sync.WaitGroup) { - op.Execute() - wg.Done() - }(&wg) - - ch <- tt.event - wg.Wait() - }) - } -} - -func createNotifyDeviceServiceDbMock() DeviceServiceLoader { - dbMock := &mocks.DeviceServiceLoader{} - dbMock.On("GetDeviceServiceById", testDeviceServiceId).Return(testDeviceService, nil) - dbMock.On("GetDeviceServiceByName", testDeviceServiceName).Return(testDeviceService, nil) - return dbMock -} - -func createNotifyDeviceServiceDbMockFail() DeviceServiceLoader { - dbMock := &mocks.DeviceServiceLoader{} - dbMock.On("GetDeviceServiceById", testDeviceServiceId).Return(models.DeviceService{}, db.ErrNotFound) - return dbMock -} - -func createNotifyEmptyAddressableDbMockFail() DeviceServiceLoader { - ds := testDeviceService - ds.Addressable = models.Addressable{} - dbMock := &mocks.DeviceServiceLoader{} - dbMock.On("GetDeviceServiceById", testDeviceServiceId).Return(ds, nil) - return dbMock -} - -var testNotificationInfo = metaConfig.NotificationInfo{ - PostDeviceChanges: true, - Slug: "device-change-", - Content: "Device update: ", - Sender: "core-metadata", - Description: "Metadata device notice", - Label: "metadata", -} - -/* - I really struggled with the question of whether to create these structs or whether to use Mockery as in the case of - the new DB interfaces. Using mockery would require that I create a new interfaces in either this package or the - metadata/interfaces package. The new interface would be local to metadata but have the same signature as the current - NotificationsClient. I decided to create the types below because it's less of a conceptual shift but also because the - interface in question has only one method. If it were a more complicated interface, thus requiring more flexible - mocking of behavior, I may have chosen to define a local interface. -*/ -type mockNotificationClientOK struct{} - -func (m mockNotificationClientOK) SendNotification(_ context.Context, _ notifications.Notification) error { - return nil -} - -type mockNotificationClientFail struct{} - -func (m mockNotificationClientFail) SendNotification(_ context.Context, _ notifications.Notification) error { - return errors.NewErrBadRequest("simulated bad request 400 response") -} - -// The NotifyLogger mock is used to register unexpected errors with the testing framework. -// See the implementation of the Error() method. -type mockNotifyLogger struct { - expectError bool - t *testing.T -} - -func newMockNotifyLogger(expectError bool, t *testing.T) logger.LoggingClient { - return mockNotifyLogger{expectError: expectError, t: t} -} - -// SetLogLevel simulates setting a log severity level -func (lc mockNotifyLogger) SetLogLevel(_ string) error { - return nil -} - -// LogLevel returns the current log level setting -func (lc mockNotifyLogger) LogLevel() string { - return "" -} - -// Info simulates logging an entry at the INFO severity level -func (lc mockNotifyLogger) Info(_ string, _ ...interface{}) { -} - -// Debug simulates logging an entry at the DEBUG severity level -func (lc mockNotifyLogger) Debug(_ string, _ ...interface{}) { -} - -// Error simulates logging an entry at the ERROR severity level -func (lc mockNotifyLogger) Error(msg string, _ ...interface{}) { - if !lc.expectError { - lc.t.Error(msg) - lc.t.Fail() - } -} - -// Trace simulates logging an entry at the TRACE severity level -func (lc mockNotifyLogger) Trace(_ string, _ ...interface{}) { -} - -// Warn simulates logging an entry at the WARN severity level -func (lc mockNotifyLogger) Warn(_ string, _ ...interface{}) { -} - -// Infof simulates logging an formatted message at the INFO severity level -func (lc mockNotifyLogger) Infof(_ string, _ ...interface{}) { -} - -// Debugf simulates logging an formatted message at the DEBUG severity level -func (lc mockNotifyLogger) Debugf(_ string, _ ...interface{}) { -} - -// Errorf simulates logging an formatted message at the ERROR severity level -func (lc mockNotifyLogger) Errorf(msg string, _ ...interface{}) { - if !lc.expectError { - lc.t.Error(msg) - lc.t.Fail() - } -} - -// Tracef simulates logging an formatted message at the TRACE severity level -func (lc mockNotifyLogger) Tracef(_ string, _ ...interface{}) { -} - -// Warnf simulates logging an formatted message at the WARN severity level -func (lc mockNotifyLogger) Warnf(_ string, _ ...interface{}) { -} diff --git a/internal/core/metadata/operators/device/request.go b/internal/core/metadata/operators/device/request.go deleted file mode 100644 index 38cc8884eb..0000000000 --- a/internal/core/metadata/operators/device/request.go +++ /dev/null @@ -1,72 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device - -import ( - "context" - "fmt" - "net/http" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" -) - -type RequestType int - -const ( - Mock RequestType = 0 - Http RequestType = 1 -) - -type Requester interface { - // Execute will invoke the supplied request. I'm not thrilled about providing the extra parameter here, but it - // follows from the net/http/Client.Do(req *Request) from the Go std lib. That is, I don't know the actual request - // to be performed until runtime and if I am to mock this properly, I don't know the request at the time of - // injection. - Execute(req *http.Request) -} - -type httpRequester struct { - client http.Client - ctx context.Context - logger logger.LoggingClient -} - -func NewRequester(key RequestType, logger logger.LoggingClient, ctx context.Context) (r Requester, err error) { - switch key { - case Http: - return httpRequester{client: http.Client{}, ctx: ctx, logger: logger}, nil - case Mock: - return mockRequester{logger: logger}, nil - default: - return nil, fmt.Errorf("unrecognized RequestType value %v", key) - } -} - -func (op httpRequester) Execute(req *http.Request) { - resp, err := op.client.Do(req) - if err == nil { - defer resp.Body.Close() - return - } - op.logger.Error(err.Error()) -} - -type mockRequester struct { - logger logger.LoggingClient -} - -func (op mockRequester) Execute(req *http.Request) { - -} diff --git a/internal/core/metadata/operators/device/request_test.go b/internal/core/metadata/operators/device/request_test.go deleted file mode 100644 index d13619d5bf..0000000000 --- a/internal/core/metadata/operators/device/request_test.go +++ /dev/null @@ -1,51 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device - -import ( - "context" - "testing" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" -) - -func TestNewRequester(t *testing.T) { - var invalidType RequestType - invalidType = 3 - - tests := []struct { - name string - rt RequestType - expectError bool - }{ - {"http_ok", Http, false}, - {"mock_ok", Mock, false}, - {"invalid_!ok", invalidType, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, err := NewRequester(tt.rt, logger.MockLogger{}, context.Background()) - if err != nil { - if !tt.expectError { - t.Errorf("%s unexpected error: %v", tt.name, err) - } - } else { - if tt.expectError { - t.Errorf("did not receive expected error: %s", tt.name) - } - } - }) - } -} diff --git a/internal/core/metadata/operators/device/update.go b/internal/core/metadata/operators/device/update.go deleted file mode 100644 index 0276770b60..0000000000 --- a/internal/core/metadata/operators/device/update.go +++ /dev/null @@ -1,199 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device - -import ( - "fmt" - "net/http" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type DeviceUpdateExecutor interface { - Execute() (err error) -} - -type updateDevice struct { - database DeviceUpdater - device contract.Device - events chan DeviceEvent - loggingClient logger.LoggingClient -} - -func NewUpdateDevice(ch chan DeviceEvent, db DeviceUpdater, d contract.Device, lc logger.LoggingClient) DeviceUpdateExecutor { - return updateDevice{database: db, events: ch, device: d, loggingClient: lc} -} - -func (op updateDevice) Execute() (err error) { - var evt DeviceEvent - - // Check if the device exists - // First try ID - var oldDevice contract.Device - oldDevice, err = op.database.GetDeviceById(op.device.Id) - if err != nil { - // Then try name - oldDevice, err = op.database.GetDeviceByName(op.device.Name) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrItemNotFound(fmt.Sprintf("device not found: %s %s", op.device.Name, op.device.Id)) - } - op.loggingClient.Error(err.Error()) - evt.Error = err - op.events <- evt - return - } - } - - // Check if the name is unique by querying for a device with the given name - // and ensuring the ID matches what's given to us as well. - var checkD contract.Device - checkD, err = op.database.GetDeviceByName(op.device.Name) - if err != nil && err != db.ErrNotFound { - op.loggingClient.Error(err.Error()) - evt.Error = err - op.events <- evt - return - } - - // Found a device, make sure its the one we're trying to update - // Different IDs -> Name is not unique - if checkD.Id != op.device.Id { - err = errors.NewErrDuplicateName("Duplicate name for Device") - op.loggingClient.Error(err.Error()) - evt.Error = err - op.events <- evt - return - } - - var service contract.DeviceService - if (op.device.Service.String() != contract.DeviceService{}.String()) { - // Check if the new service exists - // Try ID first - service, err = op.database.GetDeviceServiceById(op.device.Service.Id) - if err != nil { - // Then try name - service, err = op.database.GetDeviceServiceByName(op.device.Service.Name) - if err != nil { - op.loggingClient.Error(err.Error()) - err = errors.NewErrItemNotFound("Device service not found for updated device") - op.loggingClient.Error(err.Error()) - evt.Error = err - op.events <- evt - return - } - } - op.device.Service = service - } - - var profile contract.DeviceProfile - if (op.device.Profile.String() != contract.DeviceProfile{}.String()) { - // Check if the new profile exists - // Try ID first - profile, err = op.database.GetDeviceProfileById(op.device.Profile.Id) - if err != nil { - // Then try Name - profile, err = op.database.GetDeviceProfileByName(op.device.Profile.Name) - if err != nil { - err = errors.NewErrItemNotFound("Device profile not found for updated device") - op.loggingClient.Error(err.Error()) - evt.Error = err - op.events <- evt - return - } - } - op.device.Profile = profile - } - - if err != nil { - op.loggingClient.Error(err.Error()) - evt.Error = err - op.events <- evt - return - } - - updatedDevice := op.updateDeviceFields(oldDevice) - - if err = op.database.UpdateDevice(updatedDevice); err != nil { - op.loggingClient.Error(err.Error()) - evt.Error = err - op.events <- evt - return - } - - // Device updated successfully. Publish event onto supplied channel. - - evt.DeviceId = op.device.Id - evt.DeviceName = op.device.Name - evt.HttpMethod = http.MethodPut - evt.ServiceId = op.device.Service.Id - - if op.device.Service.Id != oldDevice.Service.Id { - evt.SecondaryServiceId = oldDevice.Service.Id - } - - op.events <- evt - - return nil -} - -func (op updateDevice) updateDeviceFields(original contract.Device) (updated contract.Device) { - updated = original - - if (op.device.Service.String() != contract.DeviceService{}.String()) { - updated.Service = op.device.Service - } - if (op.device.Profile.String() != contract.DeviceProfile{}.String()) { - updated.Profile = op.device.Profile - } - if len(op.device.Protocols) > 0 { - updated.Protocols = op.device.Protocols - } - - updated.AutoEvents = op.device.AutoEvents - - if op.device.AdminState != "" { - updated.AdminState = op.device.AdminState - } - if op.device.Description != "" { - updated.Description = op.device.Description - } - if op.device.Labels != nil { - updated.Labels = op.device.Labels - } - if op.device.LastConnected != 0 { - updated.LastConnected = op.device.LastConnected - } - if op.device.LastReported != 0 { - updated.LastReported = op.device.LastReported - } - if op.device.Location != nil { - updated.Location = op.device.Location - } - if op.device.OperatingState != contract.OperatingState("") { - updated.OperatingState = op.device.OperatingState - } - if op.device.Origin != 0 { - updated.Origin = op.device.Origin - } - if op.device.Name != "" { - updated.Name = op.device.Name - } - - return -} diff --git a/internal/core/metadata/operators/device/update_test.go b/internal/core/metadata/operators/device/update_test.go deleted file mode 100644 index 0d047f5c0d..0000000000 --- a/internal/core/metadata/operators/device/update_test.go +++ /dev/null @@ -1,174 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device - -import ( - "fmt" - "net/http" - "sync" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/device/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -func TestUpdateDevice(t *testing.T) { - tests := []struct { - name string - dbMock DeviceUpdater - expectError bool - }{ - {"UpdateDevice", createUpdateDeviceDbMock(), false}, - {"UpdateDeviceError", createUpdateDeviceErrorDbMock(), true}, - {"UpdateDeviceByName", createUpdateDeviceByNameDbMock(), false}, - {"UpdateDeviceNotFound", createUpdateDeviceNotFoundDbMock(), true}, - {"UpdateDeviceServiceByName", createUpdateDeviceByServiceNameDbMock(), false}, - {"UpdateDeviceServiceNotFound", createUpdateDeviceServiceNotFoundDbMock(), true}, - {"UpdateDeviceProfileNotFound", createUpdateDeviceDeviceProfileNotFoundDbMock(), true}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var wg sync.WaitGroup - - ch := make(chan DeviceEvent) - defer close(ch) - - wg.Add(1) - go func(wg *sync.WaitGroup, t *testing.T) { - defer wg.Done() - msg, ok := <-ch - if !ok { - t.Errorf("%s unsuccessful read from channel", t.Name()) - return - } - if msg.Error != nil && !tt.expectError { - t.Errorf("%s error reported via channel: %s", t.Name(), msg.Error.Error()) - return - } - // Ensure that all successful operations result in the correct action. - if !tt.expectError && msg.HttpMethod != http.MethodPut { - t.Errorf("Expected HTTP method 'PUT', but recieved '%s'", msg.HttpMethod) - } - }(&wg, t) - - op := NewUpdateDevice(ch, tt.dbMock, testDevice, logger.MockLogger{}) - err := op.Execute() - - if err != nil && !tt.expectError { - t.Error(err) - return - } - - if err == nil && tt.expectError { - t.Errorf("Expected an error but didn't receive one.") - return - } - - if !tt.expectError { - // assert that a call to dbClient.UpdateDevice() was made - tt.dbMock.(*mocks.DeviceUpdater).AssertCalled(t, "UpdateDevice", testDevice) - } - - wg.Wait() - }) - } -} - -func createUpdateDeviceDbMock() DeviceUpdater { - var dbMock mocks.DeviceUpdater - dbMock.On("UpdateDevice", testDevice).Return(nil) - dbMock.On("GetDeviceById", testDevice.Id).Return(testDevice, nil) - dbMock.On("GetDeviceByName", testDevice.Name).Return(testDevice, nil) - dbMock.On("GetDeviceProfileById", testDeviceProfileId).Return(testDeviceProfile, nil) - dbMock.On("GetDeviceProfileByName", testDeviceProfileName).Return(testDeviceProfile, nil) - dbMock.On("GetDeviceServiceById", testDeviceServiceId).Return(testDeviceService, nil) - dbMock.On("GetDeviceServiceByName", testDeviceServiceName).Return(testDeviceService, nil) - return &dbMock -} - -func createUpdateDeviceErrorDbMock() DeviceUpdater { - var dbMock mocks.DeviceUpdater - dbMock.On("UpdateDevice", testDevice).Return(db.ErrInvalidObjectId) - dbMock.On("GetDeviceById", testDevice.Id).Return(testDevice, nil) - dbMock.On("GetDeviceByName", testDevice.Name).Return(testDevice, nil) - dbMock.On("GetDeviceProfileById", testDeviceProfileId).Return(testDeviceProfile, nil) - dbMock.On("GetDeviceProfileByName", testDeviceProfileName).Return(testDeviceProfile, nil) - dbMock.On("GetDeviceServiceById", testDeviceServiceId).Return(testDeviceService, nil) - dbMock.On("GetDeviceServiceByName", testDeviceServiceName).Return(testDeviceService, nil) - return &dbMock -} - -func createUpdateDeviceByNameDbMock() DeviceUpdater { - var dbMock mocks.DeviceUpdater - dbMock.On("UpdateDevice", testDevice).Return(nil) - dbMock.On("GetDeviceById", testDevice.Id).Return(testDevice, fmt.Errorf("err")) - dbMock.On("GetDeviceByName", testDevice.Name).Return(testDevice, nil) - dbMock.On("GetDeviceProfileById", testDeviceProfileId).Return(testDeviceProfile, nil) - dbMock.On("GetDeviceProfileByName", testDeviceProfileName).Return(testDeviceProfile, nil) - dbMock.On("GetDeviceServiceById", testDeviceServiceId).Return(testDeviceService, nil) - dbMock.On("GetDeviceServiceByName", testDeviceServiceName).Return(testDeviceService, nil) - return &dbMock -} - -func createUpdateDeviceNotFoundDbMock() DeviceUpdater { - var dbMock mocks.DeviceUpdater - dbMock.On("UpdateDevice", testDevice).Return(nil) - dbMock.On("GetDeviceById", testDevice.Id).Return(models.Device{}, db.ErrNotFound) - dbMock.On("GetDeviceByName", testDevice.Name).Return(models.Device{}, db.ErrNotFound) - dbMock.On("GetDeviceProfileById", testDeviceProfileId).Return(testDeviceProfile, nil) - dbMock.On("GetDeviceProfileByName", testDeviceProfileName).Return(testDeviceProfile, nil) - dbMock.On("GetDeviceServiceById", testDeviceServiceId).Return(testDeviceService, nil) - dbMock.On("GetDeviceServiceByName", testDeviceServiceName).Return(testDeviceService, nil) - return &dbMock -} - -func createUpdateDeviceByServiceNameDbMock() DeviceUpdater { - var dbMock mocks.DeviceUpdater - dbMock.On("UpdateDevice", testDevice).Return(nil) - dbMock.On("GetDeviceById", testDevice.Id).Return(testDevice, nil) - dbMock.On("GetDeviceByName", testDevice.Name).Return(testDevice, nil) - dbMock.On("GetDeviceProfileById", testDeviceProfileId).Return(testDeviceProfile, nil) - dbMock.On("GetDeviceProfileByName", testDeviceProfileName).Return(testDeviceProfile, nil) - dbMock.On("GetDeviceServiceById", testDeviceServiceId).Return(testDeviceService, db.ErrNotFound) - dbMock.On("GetDeviceServiceByName", testDeviceServiceName).Return(testDeviceService, nil) - return &dbMock -} - -func createUpdateDeviceServiceNotFoundDbMock() DeviceUpdater { - var dbMock mocks.DeviceUpdater - dbMock.On("UpdateDevice", testDevice).Return(nil) - dbMock.On("GetDeviceById", testDevice.Id).Return(testDevice, nil) - dbMock.On("GetDeviceByName", testDevice.Name).Return(testDevice, nil) - dbMock.On("GetDeviceProfileById", testDeviceProfileId).Return(testDeviceProfile, nil) - dbMock.On("GetDeviceProfileByName", testDeviceProfileName).Return(testDeviceProfile, nil) - dbMock.On("GetDeviceServiceById", testDeviceServiceId).Return(testDeviceService, db.ErrNotFound) - dbMock.On("GetDeviceServiceByName", testDeviceServiceName).Return(testDeviceService, db.ErrNotFound) - return &dbMock -} - -func createUpdateDeviceDeviceProfileNotFoundDbMock() DeviceUpdater { - var dbMock mocks.DeviceUpdater - dbMock.On("UpdateDevice", testDevice).Return(nil) - dbMock.On("GetDeviceById", testDevice.Id).Return(testDevice, nil) - dbMock.On("GetDeviceByName", testDevice.Name).Return(testDevice, nil) - dbMock.On("GetDeviceProfileById", testDeviceProfileId).Return(testDeviceProfile, db.ErrNotFound) - dbMock.On("GetDeviceProfileByName", testDeviceProfileName).Return(testDeviceProfile, db.ErrNotFound) - dbMock.On("GetDeviceServiceById", testDeviceServiceId).Return(testDeviceService, nil) - dbMock.On("GetDeviceServiceByName", testDeviceServiceName).Return(testDeviceService, nil) - return &dbMock -} diff --git a/internal/core/metadata/operators/device_profile/add.go b/internal/core/metadata/operators/device_profile/add.go deleted file mode 100644 index 4f5273f534..0000000000 --- a/internal/core/metadata/operators/device_profile/add.go +++ /dev/null @@ -1,63 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device_profile - -import ( - contracts "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" -) - -type addProfileExecutor interface { - Execute() (id string, err error) -} - -type addProfile struct { - adder DeviceProfileAdder - deviceProfile contracts.DeviceProfile -} - -// Execute performs the deletion of the device profile. -func (op addProfile) Execute() (id string, err error) { - valid, err := op.deviceProfile.Validate() - if err != nil { - return "", err - } else if !valid { - // I don't think it's possible for this code to run, but we have a case for it anyway - return "", errors.NewErrDeviceProfileInvalidState(op.deviceProfile.Id, op.deviceProfile.Name, op.deviceProfile.Description) - } - - id, err = op.adder.AddDeviceProfile(op.deviceProfile) - if err != nil { - if err == db.ErrNotUnique { - return "", errors.NewErrDuplicateName("Duplicate profile name " + op.deviceProfile.Name) - } else if err == db.ErrNameEmpty { - return "", errors.NewErrEmptyDeviceProfileName() - } - - return "", err - } - - return id, nil -} - -// NewGetModelExecutor creates a new GetProfilesExecutor for retrieving device profiles by model. -func NewAddDeviceProfileExecutor(deviceProfile contracts.DeviceProfile, adder DeviceProfileAdder) addProfileExecutor { - return addProfile{ - deviceProfile: deviceProfile, - adder: adder, - } -} diff --git a/internal/core/metadata/operators/device_profile/add_test.go b/internal/core/metadata/operators/device_profile/add_test.go deleted file mode 100644 index 93e2a27fe8..0000000000 --- a/internal/core/metadata/operators/device_profile/add_test.go +++ /dev/null @@ -1,136 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device_profile - -import ( - "testing" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/device_profile/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" -) - -type mockOutline struct { - methodName string - arg interface{} - ret interface{} - err error -} - -func TestAddExecutor(t *testing.T) { - emptyName := TestDeviceProfile - emptyName.Name = "" - - duplicateCommandName := TestDeviceProfile - duplicateCommandName.CoreCommands = createCoreCommands([]contract.Command{TestCommand, TestCommand}) - - tests := []struct { - testName string - mockAdder DeviceProfileAdder - deviceProfile contract.DeviceProfile - expectedReturn string - expectedError bool - expectedErrorVal error - }{ - { - testName: "Successful database call", - mockAdder: createMockDeviceProfileAdder([]mockOutline{ - {"AddDeviceProfile", TestDeviceProfile, TestDeviceProfileID, nil}, - }), - deviceProfile: TestDeviceProfile, - expectedReturn: TestDeviceProfileID, - expectedError: false, - expectedErrorVal: nil, - }, - { - testName: "Duplicate command name", - mockAdder: createMockDeviceProfileAdder([]mockOutline{ - {"AddDeviceProfile", duplicateCommandName, "", db.ErrNotUnique}, - }), - deviceProfile: duplicateCommandName, - expectedReturn: "", - expectedError: true, - expectedErrorVal: contract.NewErrContractInvalid("duplicate names in device profile commands"), - }, - { - testName: "Duplicate device profile name", - mockAdder: createMockDeviceProfileAdder([]mockOutline{ - {"AddDeviceProfile", TestDeviceProfile, "", db.ErrNotUnique}, - }), - deviceProfile: TestDeviceProfile, - expectedReturn: "", - expectedError: true, - expectedErrorVal: errors.NewErrDuplicateName("Duplicate profile name " + TestDeviceProfileName), - }, - { - testName: "Empty device profile name", - mockAdder: createMockDeviceProfileAdder([]mockOutline{ - {"AddDeviceProfile", emptyName, "", db.ErrNameEmpty}, - }), - deviceProfile: emptyName, - expectedReturn: "", - expectedError: true, - expectedErrorVal: errors.NewErrEmptyDeviceProfileName(), - }, - { - testName: "Unsuccessful database call", - mockAdder: createMockDeviceProfileAdder([]mockOutline{ - {"AddDeviceProfile", TestDeviceProfile, "", TestError}, - }), - deviceProfile: TestDeviceProfile, - expectedReturn: "", - expectedError: true, - expectedErrorVal: TestError, - }, - } - - for _, test := range tests { - t.Run(test.testName, func(tt *testing.T) { - op := NewAddDeviceProfileExecutor(test.deviceProfile, test.mockAdder) - id, err := op.Execute() - - if test.expectedReturn != id { - t.Errorf("Observed return value doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedReturn, id) - } - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -func createMockDeviceProfileAdder(outlines []mockOutline) DeviceProfileAdder { - dbMock := mocks.DeviceProfileAdder{} - - for _, o := range outlines { - dbMock.On(o.methodName, o.arg).Return(o.ret, o.err) - } - - return &dbMock -} diff --git a/internal/core/metadata/operators/device_profile/db.go b/internal/core/metadata/operators/device_profile/db.go deleted file mode 100644 index 4e7b4c2d17..0000000000 --- a/internal/core/metadata/operators/device_profile/db.go +++ /dev/null @@ -1,68 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device_profile - -import ( - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -// DeviceLoader retrieves devices as needed from the perspective of device profiles. -type DeviceLoader interface { - GetAllDevices() ([]contract.Device, error) - GetDevicesByProfileId(pid string) ([]contract.Device, error) -} - -// DeviceProfileAdder adds DeviceProfiles to the database -type DeviceProfileAdder interface { - AddDeviceProfile(d contract.DeviceProfile) (string, error) -} - -// DeviceProfileLoader retrieves device profiles. -type DeviceProfileLoader interface { - GetDeviceProfileById(id string) (contract.DeviceProfile, error) - GetDeviceProfileByName(n string) (contract.DeviceProfile, error) - GetAllDeviceProfiles() ([]contract.DeviceProfile, error) - GetDeviceProfilesByModel(m string) ([]contract.DeviceProfile, error) - GetDeviceProfilesWithLabel(l string) ([]contract.DeviceProfile, error) - GetDeviceProfilesByManufacturerModel(man string, mod string) ([]contract.DeviceProfile, error) - GetDeviceProfilesByManufacturer(man string) ([]contract.DeviceProfile, error) -} - -// DeviceProfileDeleter deletes device profiles. -// Also provides other functionality for validating the device profile before deletion. Such as loading other entities -// to ensure there are no other dependencies on the device profile before deletion. -type DeviceProfileDeleter interface { - DeleteDeviceProfileById(id string) error - - // Functionality needed to perform validation and check state of DeviceProfile - DeviceProfileLoader - DeviceLoader - ProvisionWatcherLoader -} - -// DeviceProfileUpdater updates device profiles. -// Also provides other functionality for validating the device profile before deletion. Such as loading other entities -// to ensure there are no other dependencies on the device profile before deletion. -type DeviceProfileUpdater interface { - GetProvisionWatchersByProfileId(id string) ([]contract.ProvisionWatcher, error) - UpdateDeviceProfile(dp contract.DeviceProfile) error - DeviceProfileLoader - DeviceLoader -} - -// ProvisionWatcherLoader retrieves provision watchers. -type ProvisionWatcherLoader interface { - GetProvisionWatchersByProfileId(id string) ([]contract.ProvisionWatcher, error) -} diff --git a/internal/core/metadata/operators/device_profile/delete.go b/internal/core/metadata/operators/device_profile/delete.go deleted file mode 100644 index 9c8b3e192f..0000000000 --- a/internal/core/metadata/operators/device_profile/delete.go +++ /dev/null @@ -1,106 +0,0 @@ -package device_profile - -import ( - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" -) - -// DeleteExecutor handles the deletion of a device profile. -// Returns ErrDeviceProfileNotFound if a device profile could not be found with a matching ID nor name -// Returns ErrDeviceProfileInvalidState if the device profile has one or more devices or provision watchers -// associated with it -type DeleteExecutor interface { - Execute() error -} - -type deleteProfileById struct { - db DeviceProfileDeleter - did string -} - -// Execute performs the deletion of the device profile. -func (dpbi deleteProfileById) Execute() error { - // Check if the device profile exists - dp, err := dpbi.db.GetDeviceProfileById(dpbi.did) - if err != nil { - if err == db.ErrNotFound { - return errors.NewErrDeviceProfileNotFound(dpbi.did, "") - } - - return err - } - - // Delete the device profile - return deleteDeviceProfile(dpbi.db, dp) -} - -// NewDeleteByIDExecutor creates a new DeleteExecutor which deletes a device profile based on a device profile name. -func NewDeleteByIDExecutor(db DeviceProfileDeleter, did string) DeleteExecutor { - return deleteProfileById{ - db: db, - did: did, - } -} - -type deleteProfileByName struct { - db DeviceProfileDeleter - dn string -} - -// Execute performs the deletion of the device profile. -func (dpbn deleteProfileByName) Execute() error { - // Check if the device profile exists - dp, err := dpbn.db.GetDeviceProfileByName(dpbn.dn) - if err != nil { - if err == db.ErrNotFound { - return errors.NewErrDeviceProfileNotFound("", dpbn.dn) - } - - return err - } - - // Delete the device profile - return deleteDeviceProfile(dpbn.db, dp) -} - -// NewDeleteByNameExecutor creates a new DeleteExecutor which deletes a device profile based on a device profile ID. -func NewDeleteByNameExecutor(db DeviceProfileDeleter, dn string) DeleteExecutor { - return deleteProfileByName{ - db: db, - dn: dn, - } -} - -// Delete the device profile -// Make sure there are no devices still using it -// Delete the associated commands -func deleteDeviceProfile(dpd DeviceProfileDeleter, dp contract.DeviceProfile) error { - // Check if the device profile is still in use by devices - d, err := dpd.GetDevicesByProfileId(dp.Id) - if err != nil { - return err - } - - if len(d) > 0 { - return errors.NewErrDeviceProfileInvalidState(dp.Id, dp.Name, "Can't delete device profile, the profile is still in use by a device") - } - - // Check if the device profile is still in use by provision watchers - pw, err := dpd.GetProvisionWatchersByProfileId(dp.Id) - if err != nil { - return err - } - - if len(pw) > 0 { - return errors.NewErrDeviceProfileInvalidState(dp.Id, dp.Name, "Cant delete device profile, the profile is still in use by a provision watcher") - - } - // Delete the profile - if err := dpd.DeleteDeviceProfileById(dp.Id); err != nil { - return err - } - - return nil -} diff --git a/internal/core/metadata/operators/device_profile/delete_test.go b/internal/core/metadata/operators/device_profile/delete_test.go deleted file mode 100644 index afc4a6c025..0000000000 --- a/internal/core/metadata/operators/device_profile/delete_test.go +++ /dev/null @@ -1,251 +0,0 @@ -package device_profile - -import ( - "reflect" - "testing" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/device_profile/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" -) - -func TestDeleteProfileById(t *testing.T) { - tests := []struct { - name string - database DeviceProfileDeleter - expectError bool - expectedErrorType error - }{ - { - name: "Successful Delete", - database: createDeviceDeleter(), - expectError: false, - expectedErrorType: nil, - }, - { - name: "Device profile not found", - database: createDeviceDeleterNotFound(), - expectError: true, - expectedErrorType: errors.ErrDeviceProfileNotFound{}, - }, - { - name: "Delete error", - database: createDeviceDeleterDeleteError(), - expectError: true, - expectedErrorType: TestError, - }, - { - name: "Devices associated", - database: createDeviceDeleterWithDevicesAssociated(), - expectError: true, - expectedErrorType: errors.ErrDeviceProfileInvalidState{}, - }, - { - name: "Provision watchers associated", - database: createDeviceDeleterWithProvisionWatchersAssociated(), - expectError: true, - expectedErrorType: errors.ErrDeviceProfileInvalidState{}, - }, - { - name: "GetDeviceProfileError", - database: createDeviceDeleterGetDeviceProfileError(), - expectError: true, - expectedErrorType: TestError, - }, - - { - name: "GetProvisionWatchersByProfileIdError", - database: createDeviceDeleterGetProvisionWatchersByProfileIdError(), - expectError: true, - expectedErrorType: TestError, - }, - - { - name: "GetDevicesByProfileIdError", - database: createDeviceDeleterGetDevicesByProfileIdError(), - expectError: true, - expectedErrorType: TestError, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewDeleteByIDExecutor(test.database, TestDeviceProfile.Id) - err := op.Execute() - - if test.expectError && err == nil { - t.Error("We expected an error but did not get one") - } - - if !test.expectError && err != nil { - t.Errorf("We do not expected an error but got one. %s", err.Error()) - } - - if test.expectError { - eet := reflect.TypeOf(test.expectedErrorType) - aet := reflect.TypeOf(err) - if !aet.AssignableTo(eet) { - t.Errorf("Expected error of type %v, but got an error of type %v", eet, aet) - } - } - - return - }) - } -} -func TestDeleteProfileByName(t *testing.T) { - tests := []struct { - name string - database DeviceProfileDeleter - expectError bool - expectedErrorType error - }{ - { - name: "Successful Delete", - database: createDeviceDeleter(), - expectError: false, - expectedErrorType: nil, - }, - { - name: "Device profile not found", - database: createDeviceDeleterNotFound(), - expectError: true, - expectedErrorType: errors.ErrDeviceProfileNotFound{}, - }, - { - name: "Delete error", - database: createDeviceDeleterDeleteError(), - expectError: true, - expectedErrorType: TestError, - }, - { - name: "Devices associated", - database: createDeviceDeleterWithDevicesAssociated(), - expectError: true, - expectedErrorType: errors.ErrDeviceProfileInvalidState{}, - }, - { - name: "Provision watchers associated", - database: createDeviceDeleterWithProvisionWatchersAssociated(), - expectError: true, - expectedErrorType: errors.ErrDeviceProfileInvalidState{}, - }, - { - name: "GetDeviceProfileError", - database: createDeviceDeleterGetDeviceProfileError(), - expectError: true, - expectedErrorType: TestError, - }, - - { - name: "GetProvisionWatchersByProfileIdError", - database: createDeviceDeleterGetProvisionWatchersByProfileIdError(), - expectError: true, - expectedErrorType: TestError, - }, - - { - name: "GetDevicesByProfileIdError", - database: createDeviceDeleterGetDevicesByProfileIdError(), - expectError: true, - expectedErrorType: TestError, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewDeleteByNameExecutor(test.database, TestDeviceProfile.Name) - err := op.Execute() - - if test.expectError && err == nil { - t.Error("We expected an error but did not get one") - } - - if !test.expectError && err != nil { - t.Errorf("We do not expected an error but got one. %s", err.Error()) - } - - if test.expectError { - eet := reflect.TypeOf(test.expectedErrorType) - aet := reflect.TypeOf(err) - if !aet.AssignableTo(eet) { - t.Errorf("Expected error of type %v, but got an error of type %v", eet, aet) - } - } - - return - }) - } -} - -func createDeviceDeleter() DeviceProfileDeleter { - d := mocks.DeviceProfileDeleter{} - d.On("GetDeviceProfileById", TestDeviceProfile.Id).Return(TestDeviceProfile, nil) - d.On("GetDeviceProfileByName", TestDeviceProfile.Name).Return(TestDeviceProfile, nil) - d.On("GetDevicesByProfileId", TestDeviceProfile.Id).Return(make([]contract.Device, 0), nil) - d.On("GetProvisionWatchersByProfileId", TestDeviceProfile.Id).Return(make([]contract.ProvisionWatcher, 0), nil) - d.On("DeleteDeviceProfileById", TestDeviceProfile.Id).Return(nil) - - return &d -} - -func createDeviceDeleterNotFound() DeviceProfileDeleter { - d := mocks.DeviceProfileDeleter{} - d.On("GetDeviceProfileById", TestDeviceProfile.Id).Return(contract.DeviceProfile{}, db.ErrNotFound) - d.On("GetDeviceProfileByName", TestDeviceProfile.Name).Return(contract.DeviceProfile{}, db.ErrNotFound) - - return &d -} - -func createDeviceDeleterGetDeviceProfileError() DeviceProfileDeleter { - d := mocks.DeviceProfileDeleter{} - d.On("GetDeviceProfileById", TestDeviceProfile.Id).Return(contract.DeviceProfile{}, TestError) - d.On("GetDeviceProfileByName", TestDeviceProfile.Name).Return(contract.DeviceProfile{}, TestError) - - return &d -} - -func createDeviceDeleterWithDevicesAssociated() DeviceProfileDeleter { - d := mocks.DeviceProfileDeleter{} - d.On("GetDeviceProfileById", TestDeviceProfile.Id).Return(TestDeviceProfile, nil) - d.On("GetDeviceProfileByName", TestDeviceProfile.Name).Return(TestDeviceProfile, nil) - d.On("GetDevicesByProfileId", TestDeviceProfile.Id).Return(TestDevices, nil) - - return &d -} -func createDeviceDeleterGetDevicesByProfileIdError() DeviceProfileDeleter { - d := mocks.DeviceProfileDeleter{} - d.On("GetDeviceProfileById", TestDeviceProfile.Id).Return(TestDeviceProfile, nil) - d.On("GetDeviceProfileByName", TestDeviceProfile.Name).Return(TestDeviceProfile, nil) - d.On("GetDevicesByProfileId", TestDeviceProfile.Id).Return(make([]contract.Device, 0), TestError) - - return &d -} -func createDeviceDeleterWithProvisionWatchersAssociated() DeviceProfileDeleter { - d := mocks.DeviceProfileDeleter{} - d.On("GetDeviceProfileById", TestDeviceProfile.Id).Return(TestDeviceProfile, nil) - d.On("GetDeviceProfileByName", TestDeviceProfile.Name).Return(TestDeviceProfile, nil) - d.On("GetDevicesByProfileId", TestDeviceProfile.Id).Return(make([]contract.Device, 0), nil) - d.On("GetProvisionWatchersByProfileId", TestDeviceProfile.Id).Return(TestProvisionWatchers, nil) - - return &d -} -func createDeviceDeleterGetProvisionWatchersByProfileIdError() DeviceProfileDeleter { - d := mocks.DeviceProfileDeleter{} - d.On("GetDeviceProfileById", TestDeviceProfile.Id).Return(TestDeviceProfile, nil) - d.On("GetDeviceProfileByName", TestDeviceProfile.Name).Return(TestDeviceProfile, nil) - d.On("GetDevicesByProfileId", TestDeviceProfile.Id).Return(make([]contract.Device, 0), nil) - d.On("GetProvisionWatchersByProfileId", TestDeviceProfile.Id).Return(make([]contract.ProvisionWatcher, 0), TestError) - - return &d -} -func createDeviceDeleterDeleteError() DeviceProfileDeleter { - d := mocks.DeviceProfileDeleter{} - d.On("GetDeviceProfileById", TestDeviceProfile.Id).Return(TestDeviceProfile, nil) - d.On("GetDeviceProfileByName", TestDeviceProfile.Name).Return(TestDeviceProfile, nil) - d.On("GetDevicesByProfileId", TestDeviceProfile.Id).Return(make([]contract.Device, 0), nil) - d.On("GetProvisionWatchersByProfileId", TestDeviceProfile.Id).Return(make([]contract.ProvisionWatcher, 0), nil) - d.On("DeleteDeviceProfileById", TestDeviceProfile.Id).Return(TestError) - - return &d -} diff --git a/internal/core/metadata/operators/device_profile/get.go b/internal/core/metadata/operators/device_profile/get.go deleted file mode 100644 index e18c1882cf..0000000000 --- a/internal/core/metadata/operators/device_profile/get.go +++ /dev/null @@ -1,194 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device_profile - -import ( - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -// GetProfilesExecutor retrieves one or more device profiles based on some criteria. -type GetProfilesExecutor interface { - Execute() ([]contract.DeviceProfile, error) -} - -// GetProfilesExecutor retrieves a profile based on some criteria. -type GetProfileExecutor interface { - Execute() (contract.DeviceProfile, error) -} - -// getAllDeviceProfiles encapsulates the data needed in order to get all device profiles. -type getAllDeviceProfiles struct { - config bootstrapConfig.ServiceInfo - loader DeviceProfileLoader - logger logger.LoggingClient -} - -// Execute retrieves all device profiles. -func (g getAllDeviceProfiles) Execute() ([]contract.DeviceProfile, error) { - dps, err := g.loader.GetAllDeviceProfiles() - if err != nil { - return nil, err - } - - if len(dps) > g.config.MaxResultCount { - err = errors.NewErrLimitExceeded(g.config.MaxResultCount) - if err != nil { - g.logger.Error(err.Error()) - } - return nil, err - } - - return dps, nil -} - -// NewGetAllExecutor creates a new GetProfilesExecutor for retrieving all device profiles. -func NewGetAllExecutor( - config bootstrapConfig.ServiceInfo, - loader DeviceProfileLoader, - logger logger.LoggingClient) GetProfilesExecutor { - - return getAllDeviceProfiles{ - config: config, - loader: loader, - logger: logger, - } -} - -// getDeviceProfilesByModel encapsulates the data needed in order to get devices profiles by model. -type getDeviceProfilesByModel struct { - model string - loader DeviceProfileLoader -} - -// Execute retrieves device profiles by model. -func (g getDeviceProfilesByModel) Execute() ([]contract.DeviceProfile, error) { - return g.loader.GetDeviceProfilesByModel(g.model) -} - -// NewGetModelExecutor creates a new GetProfilesExecutor for retrieving device profiles by model. -func NewGetModelExecutor(model string, loader DeviceProfileLoader) GetProfilesExecutor { - return getDeviceProfilesByModel{ - model: model, - loader: loader, - } -} - -// getDeviceProfilesWithLabel encapsulates the data needed in order to get devices profiles by label. -type getDeviceProfilesWithLabel struct { - label string - loader DeviceProfileLoader -} - -// Execute retrieves device profiles by label. -func (g getDeviceProfilesWithLabel) Execute() ([]contract.DeviceProfile, error) { - return g.loader.GetDeviceProfilesWithLabel(g.label) -} - -// NewGetLabelExecutor creates a new GetProfilesExecutor for retrieving device profiles by label. -func NewGetLabelExecutor(label string, loader DeviceProfileLoader) GetProfilesExecutor { - return getDeviceProfilesWithLabel{ - label: label, - loader: loader, - } -} - -// getDeviceProfilesByManufacturerModel encapsulates the data needed in order to get device's profiles by -// manufacturer and model. -type getDeviceProfilesByManufacturerModel struct { - manufacturer string - model string - loader DeviceProfileLoader -} - -// Execute retrieves device profiles by manufacturer and model. -func (g getDeviceProfilesByManufacturerModel) Execute() ([]contract.DeviceProfile, error) { - return g.loader.GetDeviceProfilesByManufacturerModel(g.manufacturer, g.model) -} - -// NewGetManufacturerModelExecutor creates a new GetProfilesExecutor for retrieving device profiles by manufacturer -// and model. -func NewGetManufacturerModelExecutor( - manufacturer string, - model string, - loader DeviceProfileLoader) GetProfilesExecutor { - - return getDeviceProfilesByManufacturerModel{ - manufacturer: manufacturer, - model: model, - loader: loader, - } -} - -// getDeviceProfilesByManufacturer encapsulates the data needed in order to get devices profiles by manufacturer. -type getDeviceProfilesByManufacturer struct { - manufacturer string - loader DeviceProfileLoader -} - -// Execute retrieves device profiles by manufacturer. -func (g getDeviceProfilesByManufacturer) Execute() ([]contract.DeviceProfile, error) { - return g.loader.GetDeviceProfilesByManufacturer(g.manufacturer) -} - -// NewGetManufacturerExecutor creates a new GetProfilesExecutor for retrieving device profiles by manufacturer. -func NewGetManufacturerExecutor(manufacturer string, loader DeviceProfileLoader) GetProfilesExecutor { - return getDeviceProfilesByManufacturer{ - manufacturer: manufacturer, - loader: loader, - } -} - -// getProfileID encapsulates the data needed in order to get a devices profile by ID. -type getProfileID struct { - id string - loader DeviceProfileLoader -} - -// Execute retrieves a device profile by ID. -func (g getProfileID) Execute() (contract.DeviceProfile, error) { - return g.loader.GetDeviceProfileById(g.id) -} - -// NewGetProfileID creates a new GetProfileExecutor for retrieving device profiles by ID. -func NewGetProfileID(id string, loader DeviceProfileLoader) GetProfileExecutor { - return getProfileID{ - id: id, - loader: loader, - } -} - -// getProfileName encapsulates the data needed in order to get a devices profile by name. -type getProfileName struct { - name string - loader DeviceProfileLoader -} - -// Execute retrieves a device profile by name. -func (g getProfileName) Execute() (contract.DeviceProfile, error) { - return g.loader.GetDeviceProfileByName(g.name) -} - -// NewGetProfileName creates a new GetProfileExecutor for retrieving device profiles by name. -func NewGetProfileName(name string, loader DeviceProfileLoader) GetProfileExecutor { - return getProfileName{ - name: name, - loader: loader, - } -} diff --git a/internal/core/metadata/operators/device_profile/get_test.go b/internal/core/metadata/operators/device_profile/get_test.go deleted file mode 100644 index 50617b9c40..0000000000 --- a/internal/core/metadata/operators/device_profile/get_test.go +++ /dev/null @@ -1,381 +0,0 @@ -package device_profile - -import ( - "reflect" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/device_profile/mocks" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -var TestLabelError1 = "TestErrorLabel1" -var TestLabelError2 = "TestErrorLabel2" - -var TestDeviceProfileError = createTestDeviceProfileWithCommands("TestErrorID", "TestErrorName", []string{TestLabelError1, TestLabelError2}, "TestErrorManufacturer", "TestErrorModel", TestCommand) -var TestDeviceProfiles = []contract.DeviceProfile{ - TestDeviceProfile, - createTestDeviceProfileWithCommands("TestDeviceProfileID2", "TestDeviceProfileName2", []string{TestDeviceProfileLabel1, TestDeviceProfileLabel2}, TestDeviceProfileManufacturer, TestDeviceProfileModel, TestCommand), - createTestDeviceProfileWithCommands("TestErrorID", "TestErrorName", []string{TestLabelError1, TestLabelError2}, "TestErrorManufacturer", "TestErrorModel", TestCommand), -} - -func TestGetAllExecutor(t *testing.T) { - tests := []struct { - name string - dl DeviceProfileLoader - maxResultCount int - expectedResult []contract.DeviceProfile - expectError bool - }{ - { - "Successfully get all device profiles", - createDeviceProfileLoaderMock(), - len(TestDeviceProfiles), - TestDeviceProfiles, - false, - }, - { - "Database error", - createDeviceProfileLoaderMockGetAllError(), - len(TestDeviceProfiles), - TestDeviceProfiles, - true, - }, - { - "Max limit exceeded error", - createDeviceProfileLoaderMock(), - len(TestDeviceProfiles) - 1, - TestDeviceProfiles, - true, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewGetAllExecutor(bootstrapConfig.ServiceInfo{MaxResultCount: test.maxResultCount}, test.dl, logger.MockLogger{}) - actual, err := op.Execute() - if err != nil && !test.expectError { - t.Error(err) - return - } - - if err == nil && test.expectError { - t.Errorf("error was expected, none occurred") - return - } - - if !test.expectError && !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} -func TestGetModelExecutor(t *testing.T) { - tests := []struct { - name string - dl DeviceProfileLoader - mod string - expectedResult []contract.DeviceProfile - expectError bool - }{ - { - "Successfully get all device profiles", - createDeviceProfileLoaderMock(), - TestDeviceProfileModel, - TestDeviceProfiles, - false, - }, - { - "Database error", - createDeviceProfileLoaderMock(), - TestDeviceProfileError.Model, - nil, - true, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewGetModelExecutor(test.mod, test.dl) - actual, err := op.Execute() - if err != nil && !test.expectError { - t.Error(err) - return - } - - if err == nil && test.expectError { - t.Errorf("error was expected, none occurred") - return - } - - if !test.expectError && !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func TestGetLabelExecutor(t *testing.T) { - tests := []struct { - name string - dl DeviceProfileLoader - l string - expectedResult []contract.DeviceProfile - expectError bool - }{ - { - "Successfully get all device profiles", - createDeviceProfileLoaderMock(), - TestDeviceProfileLabel1, - TestDeviceProfiles, - false, - }, - { - "Database error", - createDeviceProfileLoaderMock(), - TestLabelError1, - nil, - true, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewGetLabelExecutor(test.l, test.dl) - actual, err := op.Execute() - if err != nil && !test.expectError { - t.Error(err) - return - } - - if err == nil && test.expectError { - t.Errorf("error was expected, none occurred") - return - } - - if !test.expectError && !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func TestGetManufacturerModelExecutor(t *testing.T) { - tests := []struct { - name string - dl DeviceProfileLoader - man string - mod string - expectedResult []contract.DeviceProfile - expectError bool - }{ - { - "Successfully get all device profiles", - createDeviceProfileLoaderMock(), - TestDeviceProfile.Manufacturer, - TestDeviceProfile.Model, - TestDeviceProfiles, - false, - }, - { - "Database error", - createDeviceProfileLoaderMock(), - TestDeviceProfileError.Manufacturer, - TestDeviceProfileError.Model, - nil, - true, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewGetManufacturerModelExecutor(test.man, test.mod, test.dl) - actual, err := op.Execute() - if err != nil && !test.expectError { - t.Error(err) - return - } - - if err == nil && test.expectError { - t.Errorf("error was expected, none occurred") - return - } - - if !test.expectError && !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func TestGetManufacturerExecutor(t *testing.T) { - tests := []struct { - name string - dl DeviceProfileLoader - man string - expectedResult []contract.DeviceProfile - expectError bool - }{ - { - "Successfully get all device profiles", - createDeviceProfileLoaderMock(), - TestDeviceProfile.Manufacturer, - TestDeviceProfiles, - false, - }, - { - "Database error", - createDeviceProfileLoaderMock(), - TestDeviceProfileError.Manufacturer, - nil, - true, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewGetManufacturerExecutor(test.man, test.dl) - actual, err := op.Execute() - if err != nil && !test.expectError { - t.Error(err) - return - } - - if err == nil && test.expectError { - t.Errorf("error was expected, none occurred") - return - } - - if !test.expectError && !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func TestGetIdExecutor(t *testing.T) { - tests := []struct { - name string - dl DeviceProfileLoader - id string - expectedResult contract.DeviceProfile - expectError bool - }{ - { - "Successfully get all device profiles", - createDeviceProfileLoaderMock(), - TestDeviceProfile.Id, - TestDeviceProfile, - false, - }, - { - "Database error", - createDeviceProfileLoaderMock(), - TestDeviceProfileError.Id, - contract.DeviceProfile{}, - true, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewGetProfileID(test.id, test.dl) - actual, err := op.Execute() - if err != nil && !test.expectError { - t.Error(err) - return - } - - if err == nil && test.expectError { - t.Errorf("error was expected, none occurred") - return - } - - if !test.expectError && !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func TestGetNameExecutor(t *testing.T) { - tests := []struct { - name string - dl DeviceProfileLoader - profileName string - expectedResult contract.DeviceProfile - expectError bool - }{ - { - "Successfully get all device profiles", - createDeviceProfileLoaderMock(), - TestDeviceProfile.Name, - TestDeviceProfile, - false, - }, - { - "Database error", - createDeviceProfileLoaderMock(), - TestDeviceProfileError.Name, - contract.DeviceProfile{}, - true, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewGetProfileName(test.profileName, test.dl) - actual, err := op.Execute() - if err != nil && !test.expectError { - t.Error(err) - return - } - - if err == nil && test.expectError { - t.Errorf("error was expected, none occurred") - return - } - - if !test.expectError && !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func createDeviceProfileLoaderMock() DeviceProfileLoader { - mock := &mocks.DeviceProfileLoader{} - - // Successful mock calls - mock.On("GetAllDeviceProfiles").Return(TestDeviceProfiles, nil) - mock.On("GetDeviceProfilesByModel", TestDeviceProfile.Model).Return(TestDeviceProfiles, nil) - mock.On("GetDeviceProfilesWithLabel", TestDeviceProfileLabel1).Return(TestDeviceProfiles, nil) - mock.On("GetDeviceProfilesWithLabel", TestDeviceProfileLabel2).Return(TestDeviceProfiles, nil) - mock.On("GetDeviceProfilesByManufacturer", TestDeviceProfile.Manufacturer).Return(TestDeviceProfiles, nil) - mock.On("GetDeviceProfilesByManufacturerModel", TestDeviceProfile.Manufacturer, TestDeviceProfile.Model).Return(TestDeviceProfiles, nil) - mock.On("GetDeviceProfileById", TestDeviceProfile.Id).Return(TestDeviceProfile, nil) - mock.On("GetDeviceProfileByName", TestDeviceProfile.Name).Return(TestDeviceProfile, nil) - - // Mock calls to simulate errors - mock.On("GetDeviceProfilesByModel", TestDeviceProfileError.Model).Return(make([]contract.DeviceProfile, 0), TestError) - mock.On("GetDeviceProfilesWithLabel", TestLabelError1).Return(make([]contract.DeviceProfile, 0), TestError) - mock.On("GetDeviceProfilesWithLabel", TestLabelError2).Return(make([]contract.DeviceProfile, 0), TestError) - mock.On("GetDeviceProfilesByManufacturer", TestDeviceProfileError.Manufacturer).Return(make([]contract.DeviceProfile, 0), TestError) - mock.On("GetDeviceProfilesByManufacturerModel", TestDeviceProfileError.Manufacturer, TestDeviceProfileError.Model).Return(make([]contract.DeviceProfile, 0), TestError) - mock.On("GetDeviceProfileById", TestDeviceProfileError.Id).Return(contract.DeviceProfile{}, TestError) - mock.On("GetDeviceProfileByName", TestDeviceProfileError.Name).Return(contract.DeviceProfile{}, TestError) - - return mock -} - -func createDeviceProfileLoaderMockGetAllError() DeviceProfileLoader { - mock := &mocks.DeviceProfileLoader{} - - // Mock calls to simulate errors - mock.On("GetAllDeviceProfiles").Return(make([]contract.DeviceProfile, 0), TestError) - - return mock -} diff --git a/internal/core/metadata/operators/device_profile/mocks/DeviceProfileAdder.go b/internal/core/metadata/operators/device_profile/mocks/DeviceProfileAdder.go deleted file mode 100644 index b5fdcc88df..0000000000 --- a/internal/core/metadata/operators/device_profile/mocks/DeviceProfileAdder.go +++ /dev/null @@ -1,32 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// DeviceProfileAdder is an autogenerated mock type for the DeviceProfileAdder type -type DeviceProfileAdder struct { - mock.Mock -} - -// AddDeviceProfile provides a mock function with given fields: d -func (_m *DeviceProfileAdder) AddDeviceProfile(d models.DeviceProfile) (string, error) { - ret := _m.Called(d) - - var r0 string - if rf, ok := ret.Get(0).(func(models.DeviceProfile) string); ok { - r0 = rf(d) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.DeviceProfile) error); ok { - r1 = rf(d) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/metadata/operators/device_profile/mocks/DeviceProfileDeleter.go b/internal/core/metadata/operators/device_profile/mocks/DeviceProfileDeleter.go deleted file mode 100644 index 515ea374b5..0000000000 --- a/internal/core/metadata/operators/device_profile/mocks/DeviceProfileDeleter.go +++ /dev/null @@ -1,251 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// DeviceProfileDeleter is an autogenerated mock type for the DeviceProfileDeleter type -type DeviceProfileDeleter struct { - mock.Mock -} - -// DeleteDeviceProfileById provides a mock function with given fields: id -func (_m *DeviceProfileDeleter) DeleteDeviceProfileById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// GetAllDeviceProfiles provides a mock function with given fields: -func (_m *DeviceProfileDeleter) GetAllDeviceProfiles() ([]models.DeviceProfile, error) { - ret := _m.Called() - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func() []models.DeviceProfile); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAllDevices provides a mock function with given fields: -func (_m *DeviceProfileDeleter) GetAllDevices() ([]models.Device, error) { - ret := _m.Called() - - var r0 []models.Device - if rf, ok := ret.Get(0).(func() []models.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Device) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfileById provides a mock function with given fields: id -func (_m *DeviceProfileDeleter) GetDeviceProfileById(id string) (models.DeviceProfile, error) { - ret := _m.Called(id) - - var r0 models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) models.DeviceProfile); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.DeviceProfile) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfileByName provides a mock function with given fields: n -func (_m *DeviceProfileDeleter) GetDeviceProfileByName(n string) (models.DeviceProfile, error) { - ret := _m.Called(n) - - var r0 models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) models.DeviceProfile); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.DeviceProfile) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfilesByManufacturer provides a mock function with given fields: man -func (_m *DeviceProfileDeleter) GetDeviceProfilesByManufacturer(man string) ([]models.DeviceProfile, error) { - ret := _m.Called(man) - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) []models.DeviceProfile); ok { - r0 = rf(man) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(man) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfilesByManufacturerModel provides a mock function with given fields: man, mod -func (_m *DeviceProfileDeleter) GetDeviceProfilesByManufacturerModel(man string, mod string) ([]models.DeviceProfile, error) { - ret := _m.Called(man, mod) - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func(string, string) []models.DeviceProfile); ok { - r0 = rf(man, mod) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(man, mod) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfilesByModel provides a mock function with given fields: m -func (_m *DeviceProfileDeleter) GetDeviceProfilesByModel(m string) ([]models.DeviceProfile, error) { - ret := _m.Called(m) - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) []models.DeviceProfile); ok { - r0 = rf(m) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(m) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfilesWithLabel provides a mock function with given fields: l -func (_m *DeviceProfileDeleter) GetDeviceProfilesWithLabel(l string) ([]models.DeviceProfile, error) { - ret := _m.Called(l) - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) []models.DeviceProfile); ok { - r0 = rf(l) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(l) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDevicesByProfileId provides a mock function with given fields: pid -func (_m *DeviceProfileDeleter) GetDevicesByProfileId(pid string) ([]models.Device, error) { - ret := _m.Called(pid) - - var r0 []models.Device - if rf, ok := ret.Get(0).(func(string) []models.Device); ok { - r0 = rf(pid) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Device) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(pid) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetProvisionWatchersByProfileId provides a mock function with given fields: id -func (_m *DeviceProfileDeleter) GetProvisionWatchersByProfileId(id string) ([]models.ProvisionWatcher, error) { - ret := _m.Called(id) - - var r0 []models.ProvisionWatcher - if rf, ok := ret.Get(0).(func(string) []models.ProvisionWatcher); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.ProvisionWatcher) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/metadata/operators/device_profile/mocks/DeviceProfileLoader.go b/internal/core/metadata/operators/device_profile/mocks/DeviceProfileLoader.go deleted file mode 100644 index 95b84d1a34..0000000000 --- a/internal/core/metadata/operators/device_profile/mocks/DeviceProfileLoader.go +++ /dev/null @@ -1,168 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// DeviceProfileLoader is an autogenerated mock type for the DeviceProfileLoader type -type DeviceProfileLoader struct { - mock.Mock -} - -// GetAllDeviceProfiles provides a mock function with given fields: -func (_m *DeviceProfileLoader) GetAllDeviceProfiles() ([]models.DeviceProfile, error) { - ret := _m.Called() - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func() []models.DeviceProfile); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfileById provides a mock function with given fields: id -func (_m *DeviceProfileLoader) GetDeviceProfileById(id string) (models.DeviceProfile, error) { - ret := _m.Called(id) - - var r0 models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) models.DeviceProfile); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.DeviceProfile) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfileByName provides a mock function with given fields: n -func (_m *DeviceProfileLoader) GetDeviceProfileByName(n string) (models.DeviceProfile, error) { - ret := _m.Called(n) - - var r0 models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) models.DeviceProfile); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.DeviceProfile) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfilesByManufacturer provides a mock function with given fields: man -func (_m *DeviceProfileLoader) GetDeviceProfilesByManufacturer(man string) ([]models.DeviceProfile, error) { - ret := _m.Called(man) - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) []models.DeviceProfile); ok { - r0 = rf(man) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(man) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfilesByManufacturerModel provides a mock function with given fields: man, mod -func (_m *DeviceProfileLoader) GetDeviceProfilesByManufacturerModel(man string, mod string) ([]models.DeviceProfile, error) { - ret := _m.Called(man, mod) - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func(string, string) []models.DeviceProfile); ok { - r0 = rf(man, mod) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(man, mod) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfilesByModel provides a mock function with given fields: m -func (_m *DeviceProfileLoader) GetDeviceProfilesByModel(m string) ([]models.DeviceProfile, error) { - ret := _m.Called(m) - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) []models.DeviceProfile); ok { - r0 = rf(m) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(m) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfilesWithLabel provides a mock function with given fields: l -func (_m *DeviceProfileLoader) GetDeviceProfilesWithLabel(l string) ([]models.DeviceProfile, error) { - ret := _m.Called(l) - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) []models.DeviceProfile); ok { - r0 = rf(l) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(l) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/metadata/operators/device_profile/mocks/DeviceProfileUpdater.go b/internal/core/metadata/operators/device_profile/mocks/DeviceProfileUpdater.go deleted file mode 100644 index 125512e85d..0000000000 --- a/internal/core/metadata/operators/device_profile/mocks/DeviceProfileUpdater.go +++ /dev/null @@ -1,251 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// DeviceProfileUpdater is an autogenerated mock type for the DeviceProfileUpdater type -type DeviceProfileUpdater struct { - mock.Mock -} - -// GetAllDeviceProfiles provides a mock function with given fields: -func (_m *DeviceProfileUpdater) GetAllDeviceProfiles() ([]models.DeviceProfile, error) { - ret := _m.Called() - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func() []models.DeviceProfile); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAllDevices provides a mock function with given fields: -func (_m *DeviceProfileUpdater) GetAllDevices() ([]models.Device, error) { - ret := _m.Called() - - var r0 []models.Device - if rf, ok := ret.Get(0).(func() []models.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Device) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfileById provides a mock function with given fields: id -func (_m *DeviceProfileUpdater) GetDeviceProfileById(id string) (models.DeviceProfile, error) { - ret := _m.Called(id) - - var r0 models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) models.DeviceProfile); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.DeviceProfile) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfileByName provides a mock function with given fields: n -func (_m *DeviceProfileUpdater) GetDeviceProfileByName(n string) (models.DeviceProfile, error) { - ret := _m.Called(n) - - var r0 models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) models.DeviceProfile); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.DeviceProfile) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfilesByManufacturer provides a mock function with given fields: man -func (_m *DeviceProfileUpdater) GetDeviceProfilesByManufacturer(man string) ([]models.DeviceProfile, error) { - ret := _m.Called(man) - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) []models.DeviceProfile); ok { - r0 = rf(man) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(man) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfilesByManufacturerModel provides a mock function with given fields: man, mod -func (_m *DeviceProfileUpdater) GetDeviceProfilesByManufacturerModel(man string, mod string) ([]models.DeviceProfile, error) { - ret := _m.Called(man, mod) - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func(string, string) []models.DeviceProfile); ok { - r0 = rf(man, mod) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(man, mod) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfilesByModel provides a mock function with given fields: m -func (_m *DeviceProfileUpdater) GetDeviceProfilesByModel(m string) ([]models.DeviceProfile, error) { - ret := _m.Called(m) - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) []models.DeviceProfile); ok { - r0 = rf(m) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(m) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceProfilesWithLabel provides a mock function with given fields: l -func (_m *DeviceProfileUpdater) GetDeviceProfilesWithLabel(l string) ([]models.DeviceProfile, error) { - ret := _m.Called(l) - - var r0 []models.DeviceProfile - if rf, ok := ret.Get(0).(func(string) []models.DeviceProfile); ok { - r0 = rf(l) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceProfile) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(l) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDevicesByProfileId provides a mock function with given fields: pid -func (_m *DeviceProfileUpdater) GetDevicesByProfileId(pid string) ([]models.Device, error) { - ret := _m.Called(pid) - - var r0 []models.Device - if rf, ok := ret.Get(0).(func(string) []models.Device); ok { - r0 = rf(pid) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Device) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(pid) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetProvisionWatchersByProfileId provides a mock function with given fields: id -func (_m *DeviceProfileUpdater) GetProvisionWatchersByProfileId(id string) ([]models.ProvisionWatcher, error) { - ret := _m.Called(id) - - var r0 []models.ProvisionWatcher - if rf, ok := ret.Get(0).(func(string) []models.ProvisionWatcher); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.ProvisionWatcher) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateDeviceProfile provides a mock function with given fields: dp -func (_m *DeviceProfileUpdater) UpdateDeviceProfile(dp models.DeviceProfile) error { - ret := _m.Called(dp) - - var r0 error - if rf, ok := ret.Get(0).(func(models.DeviceProfile) error); ok { - r0 = rf(dp) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/internal/core/metadata/operators/device_profile/mocks/ValueDescriptorAdder.go b/internal/core/metadata/operators/device_profile/mocks/ValueDescriptorAdder.go deleted file mode 100644 index 4452219b4d..0000000000 --- a/internal/core/metadata/operators/device_profile/mocks/ValueDescriptorAdder.go +++ /dev/null @@ -1,34 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import context "context" - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// ValueDescriptorAdder is an autogenerated mock type for the ValueDescriptorAdder type -type ValueDescriptorAdder struct { - mock.Mock -} - -// Add provides a mock function with given fields: vdr, ctx -func (_m *ValueDescriptorAdder) Add(ctx context.Context, vdr *models.ValueDescriptor) (string, error) { - ret := _m.Called(vdr, ctx) - - var r0 string - if rf, ok := ret.Get(0).(func(*models.ValueDescriptor, context.Context) string); ok { - r0 = rf(vdr, ctx) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(*models.ValueDescriptor, context.Context) error); ok { - r1 = rf(vdr, ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/metadata/operators/device_profile/mocks/ValueDescriptorUpdater.go b/internal/core/metadata/operators/device_profile/mocks/ValueDescriptorUpdater.go deleted file mode 100644 index 47d7b503f5..0000000000 --- a/internal/core/metadata/operators/device_profile/mocks/ValueDescriptorUpdater.go +++ /dev/null @@ -1,106 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import context "context" - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// ValueDescriptorUpdater is an autogenerated mock type for the ValueDescriptorUpdater type -type ValueDescriptorUpdater struct { - mock.Mock -} - -// Add provides a mock function with given fields: vdr, ctx -func (_m *ValueDescriptorUpdater) Add(ctx context.Context, vdr *models.ValueDescriptor) (string, error) { - ret := _m.Called(vdr, ctx) - - var r0 string - if rf, ok := ret.Get(0).(func(*models.ValueDescriptor, context.Context) string); ok { - r0 = rf(vdr, ctx) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(*models.ValueDescriptor, context.Context) error); ok { - r1 = rf(vdr, ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DeleteByName provides a mock function with given fields: name, ctx -func (_m *ValueDescriptorUpdater) DeleteByName(ctx context.Context, name string) error { - ret := _m.Called(name, ctx) - - var r0 error - if rf, ok := ret.Get(0).(func(string, context.Context) error); ok { - r0 = rf(name, ctx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Update provides a mock function with given fields: vdr, ctx -func (_m *ValueDescriptorUpdater) Update(ctx context.Context, vdr *models.ValueDescriptor) error { - ret := _m.Called(vdr, ctx) - - var r0 error - if rf, ok := ret.Get(0).(func(*models.ValueDescriptor, context.Context) error); ok { - r0 = rf(vdr, ctx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ValueDescriptorForName provides a mock function with given fields: name, ctx -func (_m *ValueDescriptorUpdater) ValueDescriptorForName(ctx context.Context, name string) (models.ValueDescriptor, error) { - ret := _m.Called(name, ctx) - - var r0 models.ValueDescriptor - if rf, ok := ret.Get(0).(func(string, context.Context) models.ValueDescriptor); ok { - r0 = rf(name, ctx) - } else { - r0 = ret.Get(0).(models.ValueDescriptor) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, context.Context) error); ok { - r1 = rf(name, ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ValueDescriptorsUsage provides a mock function with given fields: names, ctx -func (_m *ValueDescriptorUpdater) ValueDescriptorsUsage(ctx context.Context, names []string) (map[string]bool, error) { - ret := _m.Called(names, ctx) - - var r0 map[string]bool - if rf, ok := ret.Get(0).(func([]string, context.Context) map[string]bool); ok { - r0 = rf(names, ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[string]bool) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func([]string, context.Context) error); ok { - r1 = rf(names, ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/metadata/operators/device_profile/update.go b/internal/core/metadata/operators/device_profile/update.go deleted file mode 100644 index 9294fab012..0000000000 --- a/internal/core/metadata/operators/device_profile/update.go +++ /dev/null @@ -1,77 +0,0 @@ -package device_profile - -import ( - "strconv" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" -) - -// UpdateDeviceProfileExecutor provides functionality for updating and persisting device profiles. -// Returns ErrDeviceProfileNotFound if a device profile could not be found with a matching ID nor name -// Returns ErrDeviceProfileInvalidState if the device profile has one or more devices or provision watchers -// associated with it -type UpdateDeviceProfileExecutor interface { - Execute() (contract.DeviceProfile, error) -} - -// updateDeviceProfile updates device profiles by way of the operator pattern. -type updateDeviceProfile struct { - database DeviceProfileUpdater - dp contract.DeviceProfile -} - -// Execute updates and persists the device profile. -func (n updateDeviceProfile) Execute() (contract.DeviceProfile, error) { - // Check if the Device Profile exists - var existingDeviceProfile contract.DeviceProfile - // First try with ID - existingDeviceProfile, err := n.database.GetDeviceProfileById(n.dp.Id) - if err != nil { - // Try with name - existingDeviceProfile, err = n.database.GetDeviceProfileByName(n.dp.Name) - if err != nil { - return contract.DeviceProfile{}, errors.NewErrDeviceProfileNotFound(n.dp.Id, n.dp.Name) - } - } - - d, err := n.database.GetDevicesByProfileId(existingDeviceProfile.Id) - if err != nil { - return contract.DeviceProfile{}, err - } - - if len(d) > 0 { - return contract.DeviceProfile{}, errors.NewErrDeviceProfileInvalidState(n.dp.Id, n.dp.Name, strconv.Itoa(len(d))+" devices are associated with the device profile") - } - - p, err := n.database.GetProvisionWatchersByProfileId(existingDeviceProfile.Id) - if err != nil { - return contract.DeviceProfile{}, err - } - - if len(p) > 0 { - - return contract.DeviceProfile{}, errors.NewErrDeviceProfileInvalidState(n.dp.Id, n.dp.Name, strconv.Itoa(len(p))+" provision watchers are associated with the device profile") - } - - n.dp.Id = existingDeviceProfile.Id - - // For the name field only we will not process an update unless it has been explicitly provided. - if n.dp.Name == "" { - n.dp.Name = existingDeviceProfile.Name - } - - if err := n.database.UpdateDeviceProfile(n.dp); err != nil { - return contract.DeviceProfile{}, err - } - - return n.dp, nil -} - -// NewUpdateDeviceProfileExecutor creates an UpdateDeviceProfileExecutor. -func NewUpdateDeviceProfileExecutor(db DeviceProfileUpdater, dp contract.DeviceProfile) UpdateDeviceProfileExecutor { - return updateDeviceProfile{ - database: db, - dp: dp} -} diff --git a/internal/core/metadata/operators/device_profile/update_test.go b/internal/core/metadata/operators/device_profile/update_test.go deleted file mode 100644 index dc0354969f..0000000000 --- a/internal/core/metadata/operators/device_profile/update_test.go +++ /dev/null @@ -1,246 +0,0 @@ -package device_profile - -import ( - "encoding/json" - "errors" - "testing" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/device_profile/mocks" -) - -var TestError = errors.New("test error") -var TestDeviceProfileID = "TestProfileID" -var TestDeviceProfileName = "TestProfileName" -var TestDeviceProfileLabel1 = "TestLabel1" -var TestDeviceProfileLabel2 = "TestLabel2" -var TestDeviceProfileLabels = []string{TestDeviceProfileLabel1, TestDeviceProfileLabel2} -var TestDeviceProfileManufacturer = "TestManufacturer" -var TestDeviceProfileModel = "TestModel" -var TestDeviceProfile = createTestDeviceProfile() -var TestCommand = contract.Command{Name: "TestCommand", Id: "TestCommandId"} -var TestDevices = []contract.Device{ - { - Name: "TestDevice1", - }, - { - Name: "TestDevice2", - }, -} - -var TestProvisionWatchers = []contract.ProvisionWatcher{ - { - Name: "TestProvisionWatcher1", - }, - { - Name: "TestProvisionWatcher2", - }, -} - -func TestUpdateDeviceProfile(t *testing.T) { - tests := []struct { - name string - dbMock DeviceProfileUpdater - dp contract.DeviceProfile - expectError bool - }{ - { - "Update DeviceProfile", - createDBClient(TestDeviceProfile), - TestDeviceProfile, - false, - }, - { - "Update DeviceProfile with no name", - createDBClient(TestDeviceProfile), - createTestDeviceProfileWithName(""), - false, - }, - { - "Update DeviceProfile with updated name", - createDBClient(createTestDeviceProfileWithName("NewName")), - createTestDeviceProfileWithName("NewName"), - false, - }, - { - "Multiple devices associated with device profile", - createDBClientMultipleDevicesFoundError(), - TestDeviceProfile, - true, - }, - { - "Multiple provision watchers associated with device profile", - createDBClientMultipleProvisionWatchersFoundError(), - TestDeviceProfile, - true, - }, - { - "Device Profile Not Found", - createDBClientDeviceProfileNotFoundError(), - TestDeviceProfile, - true, - }, - { - "Multiple devices associated with device profile", - createDBClientMultipleDevicesFoundError(), - TestDeviceProfile, - true, - }, - { - "Multiple provision watchers associated with device profile", - createDBClientMultipleProvisionWatchersFoundError(), - TestDeviceProfile, - true, - }, - { - "GetProvisionWatchersByProfileId adder error ", - createDBClientGetProvisionWatchersByProfileIdError(), - TestDeviceProfile, - true, - }, - { - "UpdateDeviceProfile adder error ", - createDBClientUpdateDeviceProfileError(), - TestDeviceProfile, - true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - op := NewUpdateDeviceProfileExecutor(tt.dbMock, tt.dp) - _, err := op.Execute() - if err != nil && !tt.expectError { - t.Error(err) - return - } - - if err == nil && tt.expectError { - t.Errorf("error was expected, none occurred") - return - } - }) - } -} - -func createDBClient(expectedTestProfile contract.DeviceProfile) DeviceProfileUpdater { - d := &mocks.DeviceProfileUpdater{} - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(TestDeviceProfile, nil) - d.On("GetDevicesByProfileId", TestDeviceProfileID).Return(make([]contract.Device, 0), nil) - d.On("GetProvisionWatchersByProfileId", TestDeviceProfileID).Return(make([]contract.ProvisionWatcher, 0), nil) - d.On("GetAllDeviceProfiles").Return(make([]contract.DeviceProfile, 0), nil) - d.On("UpdateDeviceProfile", expectedTestProfile).Return(nil) - - return d -} - -func createDBClientDeviceProfileNotFoundError() DeviceProfileUpdater { - d := &mocks.DeviceProfileUpdater{} - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(contract.DeviceProfile{}, TestError) - d.On("GetDeviceProfileByName", TestDeviceProfileName).Return(contract.DeviceProfile{}, TestError) - - return d -} -func createDBClientMultipleDevicesFoundError() DeviceProfileUpdater { - d := &mocks.DeviceProfileUpdater{} - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(TestDeviceProfile, nil) - d.On("GetDevicesByProfileId", TestDeviceProfileID).Return(TestDevices, nil) - - return d -} - -func createDBClientMultipleProvisionWatchersFoundError() DeviceProfileUpdater { - d := &mocks.DeviceProfileUpdater{} - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(TestDeviceProfile, nil) - d.On("GetDevicesByProfileId", TestDeviceProfileID).Return(make([]contract.Device, 0), nil) - d.On("GetProvisionWatchersByProfileId", TestDeviceProfileID).Return(TestProvisionWatchers, nil) - - return d -} - -func createDBClientGetProvisionWatchersByProfileIdError() DeviceProfileUpdater { - d := &mocks.DeviceProfileUpdater{} - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(TestDeviceProfile, nil) - d.On("GetDevicesByProfileId", TestDeviceProfileID).Return(make([]contract.Device, 0), nil) - d.On("GetProvisionWatchersByProfileId", TestDeviceProfileID).Return(make([]contract.ProvisionWatcher, 0), TestError) - d.On("GetAllDeviceProfiles").Return([]contract.DeviceProfile{}, TestError) - - return d -} - -func createDBClientUpdateDeviceProfileError() DeviceProfileUpdater { - d := &mocks.DeviceProfileUpdater{} - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(TestDeviceProfile, nil) - d.On("GetDevicesByProfileId", TestDeviceProfileID).Return(make([]contract.Device, 0), nil) - d.On("GetProvisionWatchersByProfileId", TestDeviceProfileID).Return(make([]contract.ProvisionWatcher, 0), nil) - d.On("GetAllDeviceProfiles").Return(make([]contract.DeviceProfile, 0), nil) - d.On("UpdateDeviceProfile", TestDeviceProfile).Return(TestError) - - return d -} - -// createTestDeviceProfileWithName creates a device profile with the specified name. -func createTestDeviceProfileWithName(name string) contract.DeviceProfile { - dp := createTestDeviceProfile() - dp.Name = name - return dp -} - -// createTestDeviceProfile creates a device profile to be used during testing. -// This function handles some of the necessary creation nuances which need to take place for proper mocking and equality -// verifications. -func createTestDeviceProfile() contract.DeviceProfile { - return createTestDeviceProfileWithCommands(TestDeviceProfileID, TestDeviceProfileName, TestDeviceProfileLabels, TestDeviceProfileManufacturer, TestDeviceProfileModel, TestCommand) -} - -// createTestDeviceProfileWithCommands creates a device profile to be used during testing. -// This function handles some of the necessary creation nuances which need to take place for proper mocking and equality -// verifications. -func createTestDeviceProfileWithCommands(id string, name string, labels []string, manufacturer string, model string, commands ...contract.Command) contract.DeviceProfile { - return contract.DeviceProfile{ - Id: id, - Name: name, - DescribedObject: contract.DescribedObject{ - Description: "Some test data", - Timestamps: contract.Timestamps{ - Origin: 123, - Created: 456, - Modified: 789, - }, - }, - Labels: labels, - Manufacturer: manufacturer, - Model: model, - CoreCommands: createCoreCommands(commands), - DeviceResources: []contract.DeviceResource{ - { - Name: "TestDeviceResource", - }, - }, - DeviceCommands: []contract.ProfileResource{ - { - Name: "TestProfileResource", - }, - }, - } -} - -// createCoreCommands creates Command instances which can be used during testing. -// This function is necessary due to the internal field 'isValidated', which is not exported, being false when created -// manually and true when serialized. This causes the mocking infrastructure to not match when Commands are involved -// with matching parameters or verifying results. -func createCoreCommands(commands []contract.Command) []contract.Command { - cs := make([]contract.Command, 0) - for _, command := range commands { - b, _ := command.MarshalJSON() - var temp contract.Command - err := json.Unmarshal(b, &temp) - if err != nil { - panic(err.Error()) - } - - cs = append(cs, temp) - } - - return cs -} diff --git a/internal/core/metadata/operators/device_profile/value_descriptor.go b/internal/core/metadata/operators/device_profile/value_descriptor.go deleted file mode 100644 index 176cbfaf53..0000000000 --- a/internal/core/metadata/operators/device_profile/value_descriptor.go +++ /dev/null @@ -1,238 +0,0 @@ -package device_profile - -import ( - "context" - "fmt" - "strings" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - dataErrors "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" -) - -// ValueDescriptorAdder provides the necessary functionality for creating a ValueDescriptor. -type ValueDescriptorAdder interface { - Add(ctx context.Context, vdr *contract.ValueDescriptor) (string, error) -} - -// ValueDescriptorAdder provides the necessary functionality for updating a ValueDescriptor. -type ValueDescriptorUpdater interface { - ValueDescriptorsUsage(ctx context.Context, names []string) (map[string]bool, error) - Add(ctx context.Context, vdr *contract.ValueDescriptor) (string, error) - Update(ctx context.Context, vdr *contract.ValueDescriptor) error - DeleteByName(ctx context.Context, name string) error - ValueDescriptorForName(ctx context.Context, name string) (contract.ValueDescriptor, error) -} - -// ValueDescriptorAddExecutor creates ValueDescriptor(s) via the operator pattern. -type ValueDescriptorAddExecutor interface { - Execute() error -} - -type addValueDescriptor struct { - ctx context.Context - drs []contract.DeviceResource - client ValueDescriptorAdder - logger logger.LoggingClient -} - -// Execute creates the necessary ValueDescriptors for a set of DeviceResources. -func (a addValueDescriptor) Execute() error { - for _, dr := range a.drs { - - desc := contract.From(dr) - id, err := a.client.Add(a.ctx, &desc) - if err != nil { - a.logger.Error(fmt.Sprintf("Unable to create value descriptor: %s", err.Error())) - return err - } - a.logger.Debug(fmt.Sprintf("Created Value Descriptor id: %s", id)) - } - return nil -} - -// NewAddValueDescriptorExecutor creates a new ValueDescriptorAddExecutor. -func NewAddValueDescriptorExecutor( - ctx context.Context, - client ValueDescriptorAdder, - lc logger.LoggingClient, - drs ...contract.DeviceResource) ValueDescriptorAddExecutor { - - return addValueDescriptor{ - ctx: ctx, - drs: drs, - client: client, - logger: lc, - } -} - -// updateValueDescriptor encapsulates the data needed to update a value descriptor. -type updateValueDescriptor struct { - ctx context.Context - dp contract.DeviceProfile - loader DeviceProfileUpdater - client ValueDescriptorUpdater - logger logger.LoggingClient -} - -// UpdateValueDescriptorExecutor updates a value descriptor. -type UpdateValueDescriptorExecutor interface { - Execute() error -} - -// Execute updates a value descriptor with the provided information. -func (u updateValueDescriptor) Execute() error { - // Get pre-existing device profile so we can determine what to do with the device resources provided in the update. - // For example, update/create/delete. - persistedDeviceProfile, err := u.loader.GetDeviceProfileById(u.dp.Id) - if err != nil { - persistedDeviceProfile, err = u.loader.GetDeviceProfileByName(u.dp.Name) - if err == db.ErrNotFound { - return errors.NewErrDeviceProfileNotFound(u.dp.Id, u.dp.Name) - } else if err != nil { - return err - } - } - - devices, err := u.loader.GetDevicesByProfileId(persistedDeviceProfile.Id) - if err != nil { - return err - } - - // Verify the associated DeviceProfile is in an upgradeable state, which means that no devices are associated with - // it. - if len(devices) > 0 { - var associatedDeviceNames []string - for _, d := range devices { - associatedDeviceNames = append(associatedDeviceNames, d.Name) - } - - return errors.NewErrDeviceProfileInvalidState( - persistedDeviceProfile.Id, - persistedDeviceProfile.Name, - fmt.Sprintf("The DeviceProfile is in use by Device(s):[%s]", strings.Join(associatedDeviceNames, ","))) - } - - // Get names of all the device resources so we can check the valueDescriptorUsage with one call to Core-Data. - var persistedDeviceResourceNames []string - for _, persistedDeviceResource := range persistedDeviceProfile.DeviceResources { - persistedDeviceResourceNames = append(persistedDeviceResourceNames, persistedDeviceResource.Name) - } - - // Check if any of the ValueDescriptors associated with the DeviceResources are in use. - // If so return an error stating all the ValueDescriptors which are in use. - valueDescriptorUsage, err := u.client.ValueDescriptorsUsage(u.ctx, persistedDeviceResourceNames) - if err != nil { - return err - } - - var inUseValueDescriptors []string - for name, inUse := range valueDescriptorUsage { - if inUse { - inUseValueDescriptors = append(inUseValueDescriptors, name) - } - } - - if len(inUseValueDescriptors) > 0 { - return dataErrors.NewErrValueDescriptorsInUse(inUseValueDescriptors) - } - - // Based on the DeviceProfile as it is before the update, determine which operation needs to be applied to each - // ValueDescriptor to get it in the desired state which is the information passed to the update command. - create, update, deleted := determineValueDescriptor(persistedDeviceProfile, u.dp) - - // Execute the necessary operations to get the DeviceProfile to the desired state. - for _, d := range deleted { - err = u.client.DeleteByName(u.ctx, d.Name) - if err != nil { - return err - } - - } - - for _, up := range update { - v, err := u.client.ValueDescriptorForName(u.ctx, up.Name) - if err != nil { - return err - } - - up.Id = v.Id - err = u.client.Update(u.ctx, &up) - if err != nil { - return err - } - } - - for _, c := range create { - _, err = u.client.Add(u.ctx, &c) - if err != nil { - return err - } - } - - return nil -} - -// NewUpdateValueDescriptorExecutor creates a UpdateValueDescriptorExecutor which will update ValueDescriptors. -func NewUpdateValueDescriptorExecutor( - ctx context.Context, - dp contract.DeviceProfile, - loader DeviceProfileUpdater, - client ValueDescriptorUpdater, - logger logger.LoggingClient) UpdateValueDescriptorExecutor { - - return updateValueDescriptor{ - dp: dp, - loader: loader, - client: client, - logger: logger, - ctx: ctx, - } -} - -// determineValueDescriptor creates and partitions the ValueDescriptors which need to be changed given the -// existingDeviceProfile state and the desired updatedDeviceProfile state. -// -// Returns created - a slice of ValueDescriptors which need to be created. -// Returns update - a slice of ValueDescriptors which need to be updated. -// Returns deleted - a slice of ValueDescriptors which need to be deleted. -func determineValueDescriptor( - existingDeviceProfile, - updatedDeviceProfile contract.DeviceProfile) (create, update, deleted []contract.ValueDescriptor) { - - existingValueDescriptors := map[string]contract.ValueDescriptor{} - updatedValueDescriptors := map[string]contract.ValueDescriptor{} - - var vd contract.ValueDescriptor - // Extract the names from the DeviceResources. - for _, dr := range existingDeviceProfile.DeviceResources { - vd = contract.From(dr) - existingValueDescriptors[vd.Name] = vd - } - - for _, dr := range updatedDeviceProfile.DeviceResources { - vd = contract.From(dr) - updatedValueDescriptors[vd.Name] = vd - } - - // Determine which ValueDescriptors need to be update, created or deleted. - for k, v := range updatedValueDescriptors { - // If updatedDeviceProfile dr's are in existingDeviceProfile then update - if _, ok := existingValueDescriptors[k]; ok { - update = append(update, v) - } else { - create = append(create, v) - } - } - - for k, v := range existingValueDescriptors { - if _, ok := updatedValueDescriptors[k]; !ok { - deleted = append(deleted, v) - } - } - - return -} diff --git a/internal/core/metadata/operators/device_profile/value_descriptor_test.go b/internal/core/metadata/operators/device_profile/value_descriptor_test.go deleted file mode 100644 index 13cf5c0c86..0000000000 --- a/internal/core/metadata/operators/device_profile/value_descriptor_test.go +++ /dev/null @@ -1,355 +0,0 @@ -package device_profile - -import ( - "context" - "reflect" - "testing" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - dataErrors "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces" - mocks2 "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces/mocks" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/device_profile/mocks" -) - -var TestDeviceResource1 = contract.DeviceResource{ - Name: "TestDeviceResource1Name", - Description: "TestDeviceResource1Description", - Properties: contract.ProfileProperty{ - Value: contract.PropertyValue{ - Maximum: "TestValueMax", - Minimum: "TestValueMin", - DefaultValue: "TestValueDefault", - Type: "TestValueType", - FloatEncoding: "TestValueFloatEncoding", - MediaType: "TestValueMediaType", - }, - Units: contract.Units{ - Type: "TestUnitType", - DefaultValue: "TestDefaultUnitValue", - ReadWrite: "TestReadWrite", - }, - }, -} - -var TestDeviceResource2 = contract.DeviceResource{ - Name: "TestDeviceResource2Name", - Description: "TestDeviceResource2Description", - Properties: contract.ProfileProperty{ - Value: contract.PropertyValue{ - Maximum: "TestValueMax", - Minimum: "TestValueMin", - DefaultValue: "TestValueDefault", - Type: "TestValueType", - FloatEncoding: "TestValueFloatEncoding", - MediaType: "TestValueMediaType", - }, - Units: contract.Units{ - Type: "TestUnitType", - DefaultValue: "TestDefaultUnitValue", - ReadWrite: "TestReadWrite", - }, - }, -} - -var TestDeviceResourceError = contract.DeviceResource{ - Name: "TestDeviceResourceError", - Description: "TestDeviceResource1Description", - Properties: contract.ProfileProperty{ - Value: contract.PropertyValue{ - Maximum: "TestValueMax", - Minimum: "TestValueMin", - DefaultValue: "TestValueDefault", - Type: "TestValueType", - FloatEncoding: "TestValueFloatEncoding", - MediaType: "TestValueMediaType", - }, - Units: contract.Units{ - Type: "TestUnitType", - DefaultValue: "TestDefaultUnitValue", - ReadWrite: "TestReadWrite", - }, - }, -} - -var TestValueDescriptor1 = contract.From(TestDeviceResource1) -var TestValueDescriptor2 = contract.From(TestDeviceResource2) -var TestValueDescriptorError = contract.From(TestDeviceResourceError) -var TestDeviceResources = []contract.DeviceResource{ - TestDeviceResource1, - TestDeviceResource2, -} - -var TestContext = context.Background() - -var TestUpdatedDeviceProfile = contract.DeviceProfile{ - DescribedObject: contract.DescribedObject{}, - Id: "TestDeivceProfile", - Name: "TestDeviceProfileName", - DeviceResources: []contract.DeviceResource{TestCreateDeviceResource, TestUpdateDeviceResource}, -} - -var TestExistingDeviceProfile = contract.DeviceProfile{ - Id: TestUpdatedDeviceProfile.Id, - Name: TestUpdatedDeviceProfile.Name, - DeviceResources: []contract.DeviceResource{TestDeleteDeviceResource, TestUpdateDeviceResource}, -} - -var TestDeleteDeviceResource = contract.DeviceResource{Name: "TestDeleteDeviceResource"} -var TestUpdateDeviceResource = contract.DeviceResource{Name: "TestUpdateDeviceResource"} -var TestCreateDeviceResource = contract.DeviceResource{Name: "TestCreateDeviceResource"} -var TestDeleteValueDescriptor = contract.From(TestDeleteDeviceResource) -var TestUpdateValueDescriptor = contract.From(TestUpdateDeviceResource) -var TestCreateValueDescriptor = contract.From(TestCreateDeviceResource) -var TestInUseDeviceResource = contract.DeviceResource{Name: "TestInUseDeviceResource"} - -func TestAddValueDescriptors(t *testing.T) { - tests := []struct { - name string - dr []contract.DeviceResource - expectError bool - expectedErrorType error - }{ - { - "Add 1 ValueDescriptor successfully", - []contract.DeviceResource{TestDeviceResource1}, - false, - nil, - }, - { - "Add multiple ValueDescriptor successfully", - TestDeviceResources, - false, - nil, - }, - { - "Add 1 ValueDescriptor with client error", - []contract.DeviceResource{TestDeviceResourceError}, - true, - TestError, - }, - { - "Add multiple ValueDescriptor with client error", - []contract.DeviceResource{TestDeviceResource1, TestDeviceResource2, TestDeviceResourceError}, - true, - TestError, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewAddValueDescriptorExecutor(TestContext, createMockValueDescriptorAdder(), logger.MockLogger{}, test.dr...) - err := op.Execute() - - if test.expectError && err == nil { - t.Error("We expected an error but did not get one") - } - - if !test.expectError && err != nil { - t.Errorf("We do not expect an error but got one. %s", err.Error()) - } - - if test.expectError { - eet := reflect.TypeOf(test.expectedErrorType) - aet := reflect.TypeOf(err) - if !aet.AssignableTo(eet) { - t.Errorf("Expected error of type %v, but got an error of type %v", eet, aet) - } - } - - return - }) - } -} - -func TestUpdateValueDescriptors(t *testing.T) { - tests := []struct { - name string - oldDP contract.DeviceProfile - newDP contract.DeviceProfile - loader DeviceProfileUpdater - client ValueDescriptorUpdater - expectError bool - expectedErrorType error - }{ - { - "Successfully update", - TestExistingDeviceProfile, - TestUpdatedDeviceProfile, - createMockDBClient(), - createMockValueDescriptorUpdater(), - false, - nil, - }, - { - "DeviceProfile in use", - TestExistingDeviceProfile, - TestUpdatedDeviceProfile, - createMockDBClientDeviceProfileInUse(), - createMockValueDescriptorUpdater(), - true, - errors.ErrDeviceProfileInvalidState{}, - }, - { - "ValueDescriptor in use", - TestExistingDeviceProfile, - TestUpdatedDeviceProfile, - createMockDBClient(), - createMockValueDescriptorUpdaterInUseError(), - true, - dataErrors.ErrValueDescriptorsInUse{}, - }, - { - "ValueDescriptorUsage error", - TestExistingDeviceProfile, - TestUpdatedDeviceProfile, - createMockDBClient(), - createMockValueDescriptorUsageError(), - true, - TestError, - }, - { - "GetDeviceProfileByName error", - TestExistingDeviceProfile, - TestUpdatedDeviceProfile, - createMockErrorDBClient(), - createMockValueDescriptorUpdater(), - true, - TestError, - }, - { - "ValueDescriptor add error", - TestExistingDeviceProfile, - TestUpdatedDeviceProfile, - createMockDBClient(), - createMockValueDescriptorClientError(TestError, nil, nil, nil), - true, - TestError, - }, - { - "ValueDescriptor get error", - TestExistingDeviceProfile, - TestUpdatedDeviceProfile, - createMockDBClient(), - createMockValueDescriptorClientError(nil, TestError, nil, nil), - true, - TestError, - }, - { - "ValueDescriptor update error", - TestExistingDeviceProfile, - TestUpdatedDeviceProfile, - createMockDBClient(), - createMockValueDescriptorClientError(nil, nil, TestError, nil), - true, - TestError, - }, - { - "ValueDescriptor delete error", - TestExistingDeviceProfile, - TestUpdatedDeviceProfile, - createMockDBClient(), - createMockValueDescriptorClientError(nil, nil, nil, TestError), - true, - TestError, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewUpdateValueDescriptorExecutor( - TestContext, - test.newDP, - test.loader, - test.client, - logger.MockLogger{}) - err := op.Execute() - - if test.expectError && err == nil { - t.Error("We expected an error but did not get one") - } - - if !test.expectError && err != nil { - t.Errorf("We do not expect an error but got one. %s", err.Error()) - } - - if test.expectError { - eet := reflect.TypeOf(test.expectedErrorType) - aet := reflect.TypeOf(err) - if !aet.AssignableTo(eet) { - t.Errorf("Expected error of type %v, but got an error of type %v", eet, aet) - } - } - - return - }) - } -} - -func createMockValueDescriptorAdder() ValueDescriptorAdder { - mockClient := mocks.ValueDescriptorAdder{} - mockClient.On("Add", &TestValueDescriptor1, TestContext).Return("1", nil) - mockClient.On("Add", &TestValueDescriptor2, TestContext).Return("2", nil) - mockClient.On("Add", &TestValueDescriptorError, TestContext).Return("", TestError) - - return &mockClient -} - -func createMockValueDescriptorUpdater() ValueDescriptorUpdater { - mockUpdater := &mocks.ValueDescriptorUpdater{} - mockUpdater.On("ValueDescriptorsUsage", []string{TestDeleteDeviceResource.Name, TestUpdateDeviceResource.Name}, TestContext).Return(map[string]bool{TestUpdateDeviceResource.Name: false}, nil) - mockUpdater.On("Add", &TestCreateValueDescriptor, TestContext).Return(TestCreateDeviceResource.Name, nil) - mockUpdater.On("ValueDescriptorForName", TestUpdateDeviceResource.Name, TestContext).Return(TestUpdateValueDescriptor, nil) - mockUpdater.On("Update", &TestUpdateValueDescriptor, TestContext).Return(nil) - mockUpdater.On("DeleteByName", TestDeleteDeviceResource.Name, TestContext).Return(nil) - - return mockUpdater -} - -func createMockValueDescriptorUpdaterInUseError() ValueDescriptorUpdater { - mockUpdater := &mocks.ValueDescriptorUpdater{} - mockUpdater.On("ValueDescriptorsUsage", []string{TestDeleteDeviceResource.Name, TestUpdateDeviceResource.Name}, TestContext).Return(map[string]bool{TestUpdateDeviceResource.Name: true}, nil) - - return mockUpdater -} -func createMockValueDescriptorUsageError() ValueDescriptorUpdater { - mockUpdater := &mocks.ValueDescriptorUpdater{} - mockUpdater.On("ValueDescriptorsUsage", []string{TestDeleteDeviceResource.Name, TestUpdateDeviceResource.Name}, TestContext).Return(nil, TestError) - - return mockUpdater -} - -func createMockValueDescriptorClientError(addError, getError, updateError, deleteError error) ValueDescriptorUpdater { - mockUpdater := &mocks.ValueDescriptorUpdater{} - mockUpdater.On("ValueDescriptorsUsage", []string{TestDeleteDeviceResource.Name, TestUpdateDeviceResource.Name}, TestContext).Return(map[string]bool{TestUpdateDeviceResource.Name: false}, nil) - mockUpdater.On("Add", &TestCreateValueDescriptor, TestContext).Return(TestCreateDeviceResource.Name, addError) - mockUpdater.On("ValueDescriptorForName", TestUpdateDeviceResource.Name, TestContext).Return(contract.ValueDescriptor{}, getError) - mockUpdater.On("Update", &TestUpdateValueDescriptor, TestContext).Return(updateError) - mockUpdater.On("DeleteByName", TestDeleteDeviceResource.Name, TestContext).Return(deleteError) - - return mockUpdater -} - -func createMockDBClient() interfaces.DBClient { - mockDb := &mocks2.DBClient{} - mockDb.On("GetDeviceProfileById", TestExistingDeviceProfile.Id).Return(TestExistingDeviceProfile, nil) - mockDb.On("GetDevicesByProfileId", TestExistingDeviceProfile.Id).Return([]contract.Device{}, nil) - - return mockDb -} -func createMockDBClientDeviceProfileInUse() interfaces.DBClient { - mockDb := &mocks2.DBClient{} - mockDb.On("GetDeviceProfileById", TestExistingDeviceProfile.Id).Return(TestExistingDeviceProfile, nil) - mockDb.On("GetDevicesByProfileId", TestExistingDeviceProfile.Id).Return(TestDevices, nil) - - return mockDb -} - -func createMockErrorDBClient() interfaces.DBClient { - mockDb := &mocks2.DBClient{} - mockDb.On("GetDeviceProfileById", TestExistingDeviceProfile.Id).Return(contract.DeviceProfile{}, TestError) - mockDb.On("GetDeviceProfileByName", TestExistingDeviceProfile.Name).Return(contract.DeviceProfile{}, TestError) - - return mockDb -} diff --git a/internal/core/metadata/operators/device_service/db.go b/internal/core/metadata/operators/device_service/db.go deleted file mode 100644 index 7643a75a52..0000000000 --- a/internal/core/metadata/operators/device_service/db.go +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device_service - -import ( - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type DeviceServiceLoader interface { - GetAllDeviceServices() ([]contract.DeviceService, error) - GetDeviceServiceByName(n string) (contract.DeviceService, error) - GetDeviceServiceById(id string) (contract.DeviceService, error) - GetDeviceServicesByAddressableId(id string) ([]contract.DeviceService, error) - - GetAddressableById(id string) (contract.Addressable, error) - GetAddressableByName(name string) (contract.Addressable, error) -} - -type DeviceServiceUpdater interface { - UpdateDeviceService(ds contract.DeviceService) error - - DeviceServiceLoader -} diff --git a/internal/core/metadata/operators/device_service/get.go b/internal/core/metadata/operators/device_service/get.go deleted file mode 100644 index d26bc2cc4f..0000000000 --- a/internal/core/metadata/operators/device_service/get.go +++ /dev/null @@ -1,154 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device_service - -import ( - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -// DeviceServiceGetListExecutor retrieves DeviceServices according to parameters defined by the implementation. -type DeviceServiceGetListExecutor interface { - Execute() ([]contract.DeviceService, error) -} - -type deviceServiceLoadAll struct { - config bootstrapConfig.ServiceInfo - database DeviceServiceLoader - logger logger.LoggingClient -} - -// NewDeviceServiceLoadAll creates a new Executor that retrieves all DeviceService registered. -func NewDeviceServiceLoadAll(cfg bootstrapConfig.ServiceInfo, db DeviceServiceLoader, log logger.LoggingClient) DeviceServiceGetListExecutor { - return deviceServiceLoadAll{config: cfg, database: db, logger: log} -} - -// Execute performs an operation that retrieves all DeviceService registered. -func (op deviceServiceLoadAll) Execute() (services []contract.DeviceService, err error) { - services, err = op.database.GetAllDeviceServices() - if err != nil { - op.logger.Error(err.Error()) - return - } - if len(services) > op.config.MaxResultCount { - err = errors.NewErrLimitExceeded(op.config.MaxResultCount) - return []contract.DeviceService{}, err - } - return -} - -type deviceServiceLoadByAddressable struct { - id string - name string - db DeviceServiceLoader -} - -// NewDeviceServiceLoadByAddressableName creates a new Executor that retrieves all DeviceService associated with a given Addressable name. -func NewDeviceServiceLoadByAddressableName(name string, db DeviceServiceLoader) DeviceServiceGetListExecutor { - return deviceServiceLoadByAddressable{name: name, db: db} -} - -// NewDeviceServiceLoadByAddressableID creates a new Executor that retrieves all DeviceService associated with a given Addressable ID. -func NewDeviceServiceLoadByAddressableID(id string, db DeviceServiceLoader) DeviceServiceGetListExecutor { - return deviceServiceLoadByAddressable{id: id, db: db} -} - -// Execute performs an operation that retrieves all DeviceService associated with a given Addressable. -func (op deviceServiceLoadByAddressable) Execute() ([]contract.DeviceService, error) { - var addr contract.Addressable - var err error - - // Check if the Addressable exists - // determine whether we're doing a lookup by ID or name - if op.id != "" { - addr, err = op.db.GetAddressableById(op.id) - if err == db.ErrNotFound { - err = errors.NewErrItemNotFound(op.id) - } - } else { - addr, err = op.db.GetAddressableByName(op.name) - if err == db.ErrNotFound { - err = errors.NewErrItemNotFound(op.name) - } - } - - if err != nil { - return nil, err - } - - if ds, err := op.db.GetDeviceServicesByAddressableId(addr.Id); err != nil { - return nil, err - } else { - return ds, nil - } -} - -// DeviceServiceGetExecutor retrieves DeviceService according to parameters defined by the implementation. -type DeviceServiceGetExecutor interface { - Execute() (contract.DeviceService, error) -} - -type deviceServiceLoadById struct { - id string - db DeviceServiceLoader -} - -// NewDeviceServiceLoadById creates a new Executor that retrieves the DeviceService associated with a given ID. -func NewDeviceServiceLoadById(id string, db DeviceServiceLoader) DeviceServiceGetExecutor { - return deviceServiceLoadById{id: id, db: db} -} - -// Execute performs an operation that retrieves the DeviceService associated with a given ID. -func (op deviceServiceLoadById) Execute() (contract.DeviceService, error) { - ds, err := op.db.GetDeviceServiceById(op.id) - if err != nil { - if err == db.ErrNotFound { - return contract.DeviceService{}, errors.NewErrItemNotFound(op.id) - } else { - return contract.DeviceService{}, err - } - } - - return ds, nil -} - -type deviceServiceLoadByName struct { - name string - db DeviceServiceLoader -} - -// NewDeviceServiceLoadByName creates a new Executor that retrieves the DeviceService associated with a given name. -func NewDeviceServiceLoadByName(name string, db DeviceServiceLoader) DeviceServiceGetExecutor { - return deviceServiceLoadByName{name: name, db: db} -} - -// Execute performs an operation that retrieves the DeviceService associated with a given name. -func (op deviceServiceLoadByName) Execute() (contract.DeviceService, error) { - ds, err := op.db.GetDeviceServiceByName(op.name) - if err != nil { - if err == db.ErrNotFound { - return contract.DeviceService{}, errors.NewErrItemNotFound(op.name) - } else { - return contract.DeviceService{}, err - } - } - - return ds, nil -} diff --git a/internal/core/metadata/operators/device_service/get_test.go b/internal/core/metadata/operators/device_service/get_test.go deleted file mode 100644 index 27a55923cc..0000000000 --- a/internal/core/metadata/operators/device_service/get_test.go +++ /dev/null @@ -1,417 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device_service - -import ( - goErrors "errors" - "reflect" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/device_service/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/google/uuid" - "github.com/stretchr/testify/mock" -) - -func TestGetAllDeviceServices(t *testing.T) { - tests := []struct { - name string - cfg bootstrapConfig.ServiceInfo - dbMock DeviceServiceLoader - expectError bool - }{ - { - "GetAllPass", - bootstrapConfig.ServiceInfo{MaxResultCount: 1}, - createMockLoader([]mockOutline{ - {"GetAllDeviceServices", mock.Anything, testDeviceServices, nil}, - }), - false, - }, - { - "GetAllFailCount", - bootstrapConfig.ServiceInfo{}, - createMockLoader([]mockOutline{ - {"GetAllDeviceServices", mock.Anything, testDeviceServices, nil}, - }), - true, - }, - { - "GetAllFailUnexpected", - bootstrapConfig.ServiceInfo{MaxResultCount: 1}, - createMockLoader([]mockOutline{ - {"GetAllDeviceServices", mock.Anything, nil, testError}, - }), - true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - op := NewDeviceServiceLoadAll(tt.cfg, tt.dbMock, logger.MockLogger{}) - _, err := op.Execute() - if err != nil && !tt.expectError { - t.Error(err) - return - } - if err == nil && tt.expectError { - t.Errorf("error was expected, none occurred") - return - } - }) - } -} - -func TestGetDeviceServiceByName(t *testing.T) { - tests := []struct { - name string - mockLoader DeviceServiceLoader - expectedVal contract.DeviceService - expectedError bool - expectedErrorVal error - }{ - { - name: "Successful database call", - mockLoader: createMockLoader([]mockOutline{ - {"GetDeviceServiceByName", testDeviceServiceName, testDeviceService, nil}, - }), - expectedVal: testDeviceService, - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "Device service not found", - mockLoader: createMockLoader([]mockOutline{ - {"GetDeviceServiceByName", testDeviceServiceName, contract.DeviceService{}, db.ErrNotFound}, - }), - expectedVal: contract.DeviceService{}, - expectedError: true, - expectedErrorVal: errors.NewErrItemNotFound(testDeviceServiceName), - }, - { - name: "Device services lookup error", - mockLoader: createMockLoader([]mockOutline{ - {"GetDeviceServiceByName", testDeviceServiceName, contract.DeviceService{}, testError}, - }), - expectedVal: contract.DeviceService{}, - expectedError: true, - expectedErrorVal: testError, - }, - } - - for _, test := range tests { - t.Run(test.name, func(tt *testing.T) { - op := NewDeviceServiceLoadByName(testDeviceServiceName, test.mockLoader) - actualVal, err := op.Execute() - if !reflect.DeepEqual(test.expectedVal, actualVal) { - t.Errorf("Observed value doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedVal, actualVal) - return - } - - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -func TestGetDeviceServiceById(t *testing.T) { - tests := []struct { - name string - mockLoader DeviceServiceLoader - expectedVal contract.DeviceService - expectedError bool - expectedErrorVal error - }{ - { - name: "Successful database call", - mockLoader: createMockLoader([]mockOutline{ - {"GetDeviceServiceById", testDeviceServiceId, testDeviceService, nil}, - }), - expectedVal: testDeviceService, - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "Device service not found", - mockLoader: createMockLoader([]mockOutline{ - {"GetDeviceServiceById", testDeviceServiceId, contract.DeviceService{}, db.ErrNotFound}, - }), - expectedVal: contract.DeviceService{}, - expectedError: true, - expectedErrorVal: errors.NewErrItemNotFound(testDeviceServiceId), - }, - { - name: "Device services lookup error", - mockLoader: createMockLoader([]mockOutline{ - {"GetDeviceServiceById", testDeviceServiceId, contract.DeviceService{}, testError}, - }), - expectedVal: contract.DeviceService{}, - expectedError: true, - expectedErrorVal: testError, - }, - } - - for _, test := range tests { - t.Run(test.name, func(tt *testing.T) { - op := NewDeviceServiceLoadById(testDeviceServiceId, test.mockLoader) - actualVal, err := op.Execute() - if !reflect.DeepEqual(test.expectedVal, actualVal) { - t.Errorf("Observed value doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedVal, actualVal) - return - } - - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -func TestGetDeviceServiceByAddressableId(t *testing.T) { - tests := []struct { - name string - mockLoader DeviceServiceLoader - value string - expectedVal []contract.DeviceService - expectedError bool - expectedErrorVal error - }{ - { - name: "Successful database call", - mockLoader: createMockLoader([]mockOutline{ - {"GetAddressableById", testDeviceServiceId, testAddressable, nil}, - {"GetDeviceServicesByAddressableId", testDeviceServiceId, testDeviceServices, nil}, - }), - value: testDeviceServiceId, - expectedVal: testDeviceServices, - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "No ID provided", - mockLoader: createMockLoader([]mockOutline{ - {"GetAddressableByName", "", contract.Addressable{}, db.ErrNotFound}, - }), - value: "", - expectedVal: nil, - expectedError: true, - expectedErrorVal: errors.NewErrItemNotFound(""), - }, - { - name: "Addressable not found", - mockLoader: createMockLoader([]mockOutline{ - {"GetAddressableById", testDeviceServiceId, contract.Addressable{}, db.ErrNotFound}, - }), - value: testDeviceServiceId, - expectedVal: nil, - expectedError: true, - expectedErrorVal: errors.NewErrItemNotFound(testDeviceServiceId), - }, - { - name: "Addressable lookup error", - mockLoader: createMockLoader([]mockOutline{ - {"GetAddressableById", testDeviceServiceId, contract.Addressable{}, testError}, - }), - value: testDeviceServiceId, - expectedVal: nil, - expectedError: true, - expectedErrorVal: testError, - }, - { - name: "Device services lookup error", - mockLoader: createMockLoader([]mockOutline{ - {"GetAddressableById", testDeviceServiceId, testAddressable, nil}, - {"GetDeviceServicesByAddressableId", testDeviceServiceId, nil, testError}, - }), - value: testDeviceServiceId, - expectedVal: nil, - expectedError: true, - expectedErrorVal: testError, - }, - } - - for _, test := range tests { - t.Run(test.name, func(tt *testing.T) { - op := NewDeviceServiceLoadByAddressableID(test.value, test.mockLoader) - actualVal, err := op.Execute() - if !reflect.DeepEqual(test.expectedVal, actualVal) { - t.Errorf("Observed value doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedVal, actualVal) - return - } - - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -func TestGetDeviceServiceByAddressableName(t *testing.T) { - tests := []struct { - name string - mockLoader DeviceServiceLoader - value string - expectedVal []contract.DeviceService - expectedError bool - expectedErrorVal error - }{ - { - name: "Successful database call", - mockLoader: createMockLoader([]mockOutline{ - {"GetAddressableByName", testDeviceServiceName, testAddressable, nil}, - {"GetDeviceServicesByAddressableId", testDeviceServiceId, testDeviceServices, nil}, - }), - value: testDeviceServiceName, - expectedVal: testDeviceServices, - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "No name provided", - mockLoader: createMockLoader([]mockOutline{ - {"GetAddressableByName", "", contract.Addressable{}, db.ErrNotFound}, - }), - value: "", - expectedVal: nil, - expectedError: true, - expectedErrorVal: errors.NewErrItemNotFound(""), - }, - { - name: "Addressable not found", - mockLoader: createMockLoader([]mockOutline{ - {"GetAddressableByName", testDeviceServiceName, contract.Addressable{}, db.ErrNotFound}, - }), - value: testDeviceServiceName, - expectedVal: nil, - expectedError: true, - expectedErrorVal: errors.NewErrItemNotFound(testDeviceServiceName), - }, - { - name: "Addressable lookup error", - mockLoader: createMockLoader([]mockOutline{ - {"GetAddressableByName", testDeviceServiceName, contract.Addressable{}, testError}, - }), - value: testDeviceServiceName, - expectedVal: nil, - expectedError: true, - expectedErrorVal: testError, - }, - { - name: "Device services lookup error", - mockLoader: createMockLoader([]mockOutline{ - {"GetAddressableByName", testDeviceServiceName, testAddressable, nil}, - {"GetDeviceServicesByAddressableId", testDeviceServiceId, nil, testError}, - }), - value: testDeviceServiceName, - expectedVal: nil, - expectedError: true, - expectedErrorVal: testError, - }, - } - - for _, test := range tests { - t.Run(test.name, func(tt *testing.T) { - op := NewDeviceServiceLoadByAddressableName(test.value, test.mockLoader) - actualVal, err := op.Execute() - if !reflect.DeepEqual(test.expectedVal, actualVal) { - t.Errorf("Observed value doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedVal, actualVal) - return - } - - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -type mockOutline struct { - methodName string - arg interface{} - ret interface{} - err error -} - -func createMockLoader(outlines []mockOutline) DeviceServiceLoader { - dbMock := mocks.DeviceServiceLoader{} - - for _, o := range outlines { - dbMock.On(o.methodName, o.arg).Return(o.ret, o.err) - } - - return &dbMock -} - -var testAddressable = contract.Addressable{Id: testDeviceServiceId, Name: testDeviceServiceName} -var testDeviceServiceId = uuid.New().String() -var testDeviceServiceName = "test service" -var testDeviceService = contract.DeviceService{Id: testDeviceServiceId, Name: testDeviceServiceName} -var testDeviceServices = []contract.DeviceService{testDeviceService} -var testError = goErrors.New("some error") diff --git a/internal/core/metadata/operators/device_service/mocks/DeviceServiceLoader.go b/internal/core/metadata/operators/device_service/mocks/DeviceServiceLoader.go deleted file mode 100644 index 19f0f9ca5f..0000000000 --- a/internal/core/metadata/operators/device_service/mocks/DeviceServiceLoader.go +++ /dev/null @@ -1,141 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// DeviceServiceLoader is an autogenerated mock type for the DeviceServiceLoader type -type DeviceServiceLoader struct { - mock.Mock -} - -// GetAddressableById provides a mock function with given fields: id -func (_m *DeviceServiceLoader) GetAddressableById(id string) (models.Addressable, error) { - ret := _m.Called(id) - - var r0 models.Addressable - if rf, ok := ret.Get(0).(func(string) models.Addressable); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Addressable) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressableByName provides a mock function with given fields: id -func (_m *DeviceServiceLoader) GetAddressableByName(id string) (models.Addressable, error) { - ret := _m.Called(id) - - var r0 models.Addressable - if rf, ok := ret.Get(0).(func(string) models.Addressable); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Addressable) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAllDeviceServices provides a mock function with given fields: -func (_m *DeviceServiceLoader) GetAllDeviceServices() ([]models.DeviceService, error) { - ret := _m.Called() - - var r0 []models.DeviceService - if rf, ok := ret.Get(0).(func() []models.DeviceService); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceService) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceServiceById provides a mock function with given fields: id -func (_m *DeviceServiceLoader) GetDeviceServiceById(id string) (models.DeviceService, error) { - ret := _m.Called(id) - - var r0 models.DeviceService - if rf, ok := ret.Get(0).(func(string) models.DeviceService); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.DeviceService) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceServiceByName provides a mock function with given fields: n -func (_m *DeviceServiceLoader) GetDeviceServiceByName(n string) (models.DeviceService, error) { - ret := _m.Called(n) - - var r0 models.DeviceService - if rf, ok := ret.Get(0).(func(string) models.DeviceService); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.DeviceService) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceServicesByAddressableId provides a mock function with given fields: id -func (_m *DeviceServiceLoader) GetDeviceServicesByAddressableId(id string) ([]models.DeviceService, error) { - ret := _m.Called(id) - - var r0 []models.DeviceService - if rf, ok := ret.Get(0).(func(string) []models.DeviceService); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceService) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/core/metadata/operators/device_service/mocks/DeviceServiceUpdater.go b/internal/core/metadata/operators/device_service/mocks/DeviceServiceUpdater.go deleted file mode 100644 index ec25a18513..0000000000 --- a/internal/core/metadata/operators/device_service/mocks/DeviceServiceUpdater.go +++ /dev/null @@ -1,155 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// DeviceServiceUpdater is an autogenerated mock type for the DeviceServiceUpdater type -type DeviceServiceUpdater struct { - mock.Mock -} - -// GetAddressableById provides a mock function with given fields: id -func (_m *DeviceServiceUpdater) GetAddressableById(id string) (models.Addressable, error) { - ret := _m.Called(id) - - var r0 models.Addressable - if rf, ok := ret.Get(0).(func(string) models.Addressable); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Addressable) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAddressableByName provides a mock function with given fields: id -func (_m *DeviceServiceUpdater) GetAddressableByName(id string) (models.Addressable, error) { - ret := _m.Called(id) - - var r0 models.Addressable - if rf, ok := ret.Get(0).(func(string) models.Addressable); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Addressable) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAllDeviceServices provides a mock function with given fields: -func (_m *DeviceServiceUpdater) GetAllDeviceServices() ([]models.DeviceService, error) { - ret := _m.Called() - - var r0 []models.DeviceService - if rf, ok := ret.Get(0).(func() []models.DeviceService); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceService) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceServiceById provides a mock function with given fields: id -func (_m *DeviceServiceUpdater) GetDeviceServiceById(id string) (models.DeviceService, error) { - ret := _m.Called(id) - - var r0 models.DeviceService - if rf, ok := ret.Get(0).(func(string) models.DeviceService); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.DeviceService) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceServiceByName provides a mock function with given fields: n -func (_m *DeviceServiceUpdater) GetDeviceServiceByName(n string) (models.DeviceService, error) { - ret := _m.Called(n) - - var r0 models.DeviceService - if rf, ok := ret.Get(0).(func(string) models.DeviceService); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(models.DeviceService) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeviceServicesByAddressableId provides a mock function with given fields: id -func (_m *DeviceServiceUpdater) GetDeviceServicesByAddressableId(id string) ([]models.DeviceService, error) { - ret := _m.Called(id) - - var r0 []models.DeviceService - if rf, ok := ret.Get(0).(func(string) []models.DeviceService); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.DeviceService) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateDeviceService provides a mock function with given fields: ds -func (_m *DeviceServiceUpdater) UpdateDeviceService(ds models.DeviceService) error { - ret := _m.Called(ds) - - var r0 error - if rf, ok := ret.Get(0).(func(models.DeviceService) error); ok { - r0 = rf(ds) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/internal/core/metadata/operators/device_service/update.go b/internal/core/metadata/operators/device_service/update.go deleted file mode 100644 index f5ed3f4ec6..0000000000 --- a/internal/core/metadata/operators/device_service/update.go +++ /dev/null @@ -1,205 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device_service - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -// UpdateAdminOrOperatingStateExecutor updates a device service's AdminState or OperatingState fields. -type UpdateAdminOrOperatingStateExecutor interface { - Execute() error -} - -type deviceServiceOpStateUpdateById struct { - id string - os contract.OperatingState - db DeviceServiceUpdater -} - -// NewUpdateOpStateByIdExecutor updates a device service's OperatingState, referencing the DeviceService by ID. -func NewUpdateOpStateByIdExecutor(id string, os contract.OperatingState, db DeviceServiceUpdater) UpdateAdminOrOperatingStateExecutor { - return deviceServiceOpStateUpdateById{id: id, os: os, db: db} -} - -// Execute updates the device service OperatingState. -func (op deviceServiceOpStateUpdateById) Execute() error { - // Check if the device service exists - ds, err := op.db.GetDeviceServiceById(op.id) - if err != nil { - if err == db.ErrNotFound { - return errors.NewErrItemNotFound(op.id) - } - - return err - } - - ds.OperatingState = op.os - if err = op.db.UpdateDeviceService(ds); err != nil { - return err - } - - return nil -} - -type deviceServiceOpStateUpdateByName struct { - name string - os contract.OperatingState - db DeviceServiceUpdater -} - -// NewUpdateOpStateByNameExecutor updates a device service's OperatingState, referencing the DeviceService by name. -func NewUpdateOpStateByNameExecutor(name string, os contract.OperatingState, db DeviceServiceUpdater) UpdateAdminOrOperatingStateExecutor { - return deviceServiceOpStateUpdateByName{name: name, os: os, db: db} -} - -// Execute updates the device service OperatingState. -func (op deviceServiceOpStateUpdateByName) Execute() error { - // Check if the device service exists - ds, err := op.db.GetDeviceServiceByName(op.name) - if err != nil { - if err == db.ErrNotFound { - return errors.NewErrItemNotFound(op.name) - } - - return err - } - - ds.OperatingState = op.os - if err = op.db.UpdateDeviceService(ds); err != nil { - return err - } - - return nil -} - -type deviceServiceAdminStateUpdateById struct { - id string - as contract.AdminState - db DeviceServiceUpdater - lc logger.LoggingClient -} - -// NewUpdateAdminStateByIdExecutor updates a device service's AdminState, referencing the DeviceService by ID. -func NewUpdateAdminStateByIdExecutor(id string, as contract.AdminState, db DeviceServiceUpdater, lc logger.LoggingClient) UpdateAdminOrOperatingStateExecutor { - return deviceServiceAdminStateUpdateById{id: id, as: as, db: db, lc: lc} -} - -// Execute updates the device service AdminState. -func (op deviceServiceAdminStateUpdateById) Execute() error { - // Check if the device service exists - ds, err := op.db.GetDeviceServiceById(op.id) - if err != nil { - if err == db.ErrNotFound { - return errors.NewErrItemNotFound(op.id) - } - - return err - } - - ds.AdminState = op.as - if err = op.db.UpdateDeviceService(ds); err != nil { - return err - } - - go adminStateCallback(ds, op.lc) - - return nil -} - -type deviceServiceAdminStateUpdateByName struct { - name string - as contract.AdminState - db DeviceServiceUpdater - lc logger.LoggingClient -} - -// NewUpdateAdminStateByNameExecutor updates a device service's AdminState, referencing the DeviceService by name. -func NewUpdateAdminStateByNameExecutor(name string, as contract.AdminState, db DeviceServiceUpdater, lc logger.LoggingClient) UpdateAdminOrOperatingStateExecutor { - return deviceServiceAdminStateUpdateByName{name: name, as: as, db: db, lc: lc} -} - -// Execute updates the device service AdminState. -func (op deviceServiceAdminStateUpdateByName) Execute() error { - // Check if the device service exists - ds, err := op.db.GetDeviceServiceByName(op.name) - if err != nil { - if err == db.ErrNotFound { - return errors.NewErrItemNotFound(op.name) - } - - return err - } - - ds.AdminState = op.as - if err = op.db.UpdateDeviceService(ds); err != nil { - return err - } - - go adminStateCallback(ds, op.lc) - - return nil -} - -func adminStateCallback( - service contract.DeviceService, - lc logger.LoggingClient) { - - if len(service.Addressable.GetCallbackURL()) == 0 { - return - } - - req, err := createCallbackRequest(http.MethodPut, service) - if err != nil { - lc.Error(fmt.Sprintf("fail to create callback request for %s", service.Name)) - } - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - lc.Error(fmt.Sprintf("fail to invoke callback for %s, %v", service.Name, err)) - return - } else if resp.StatusCode != http.StatusOK { - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - lc.Error(fmt.Sprintf("fail to read response body, %v", err)) - } - lc.Error(fmt.Sprintf("fail to invoke callback for %s, %s", service.Name, string(b))) - } - resp.Body.Close() - resp.Close = true -} - -func createCallbackRequest(httpMethod string, service contract.DeviceService) (*http.Request, error) { - body, err := json.Marshal(contract.CallbackAlert{ActionType: contract.SERVICE, Id: service.Id}) - if err != nil { - return nil, err - } - req, err := http.NewRequest(httpMethod, service.Addressable.GetCallbackURL(), bytes.NewReader(body)) - if err != nil { - return nil, err - } - req.Header.Add(clients.ContentType, clients.ContentTypeJSON) - return req, nil -} diff --git a/internal/core/metadata/operators/device_service/update_test.go b/internal/core/metadata/operators/device_service/update_test.go deleted file mode 100644 index ff9e0737f9..0000000000 --- a/internal/core/metadata/operators/device_service/update_test.go +++ /dev/null @@ -1,301 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 device_service - -import ( - "testing" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/device_service/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -func TestUpdateOperatingStateByIdExecutor(t *testing.T) { - operatingStateEnabled := testDeviceService - operatingStateEnabled.OperatingState = testOperatingState - - tests := []struct { - name string - mockUpdater DeviceServiceUpdater - expectedError bool - expectedErrorVal error - }{ - { - name: "Successful database call", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetDeviceServiceById", testDeviceServiceId, testDeviceService, nil}, - {"UpdateDeviceService", operatingStateEnabled, nil, nil}}), - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "Device service not found", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetDeviceServiceById", testDeviceServiceId, contract.DeviceService{}, db.ErrNotFound}}), - expectedError: true, - expectedErrorVal: errors.NewErrItemNotFound(testDeviceServiceId), - }, - { - name: "Device service lookup error", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetDeviceServiceById", testDeviceServiceId, contract.DeviceService{}, testError}}), - expectedError: true, - expectedErrorVal: testError, - }, - { - name: "Update error", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetDeviceServiceById", testDeviceServiceId, testDeviceService, nil}, - {"UpdateDeviceService", operatingStateEnabled, testError, nil}}), - expectedError: true, - expectedErrorVal: testError, - }, - } - - for _, test := range tests { - t.Run(test.name, func(tt *testing.T) { - op := NewUpdateOpStateByIdExecutor(testDeviceServiceId, testOperatingState, test.mockUpdater) - err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -func TestUpdateOperatingStateByNameExecutor(t *testing.T) { - operatingStateEnabled := testDeviceService - operatingStateEnabled.OperatingState = testOperatingState - - tests := []struct { - name string - mockUpdater DeviceServiceUpdater - expectedError bool - expectedErrorVal error - }{ - { - name: "Successful database call", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetDeviceServiceByName", testDeviceServiceName, testDeviceService, nil}, - {"UpdateDeviceService", operatingStateEnabled, nil, nil}}), - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "Device service not found", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetDeviceServiceByName", testDeviceServiceName, contract.DeviceService{}, db.ErrNotFound}}), - expectedError: true, - expectedErrorVal: errors.NewErrItemNotFound(testDeviceServiceName), - }, - { - name: "Device service lookup error", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetDeviceServiceByName", testDeviceServiceName, contract.DeviceService{}, testError}}), - expectedError: true, - expectedErrorVal: testError, - }, - { - name: "Update error", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetDeviceServiceByName", testDeviceServiceName, testDeviceService, nil}, - {"UpdateDeviceService", operatingStateEnabled, testError, nil}}), - expectedError: true, - expectedErrorVal: testError, - }, - } - - for _, test := range tests { - t.Run(test.name, func(tt *testing.T) { - op := NewUpdateOpStateByNameExecutor(testDeviceServiceName, testOperatingState, test.mockUpdater) - err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -func TestUpdateAdminStateByIdExecutor(t *testing.T) { - adminStateUnlocked := testDeviceService - adminStateUnlocked.AdminState = testAdminState - lc := logger.MockLogger{} - - tests := []struct { - name string - mockUpdater DeviceServiceUpdater - expectedError bool - expectedErrorVal error - }{ - { - name: "Successful database call", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetDeviceServiceById", testDeviceServiceId, testDeviceService, nil}, - {"UpdateDeviceService", adminStateUnlocked, nil, nil}}), - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "Device service not found", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetDeviceServiceById", testDeviceServiceId, contract.DeviceService{}, db.ErrNotFound}}), - expectedError: true, - expectedErrorVal: errors.NewErrItemNotFound(testDeviceServiceId), - }, - { - name: "Device service lookup error", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetDeviceServiceById", testDeviceServiceId, contract.DeviceService{}, testError}}), - expectedError: true, - expectedErrorVal: testError, - }, - { - name: "Update error", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetDeviceServiceById", testDeviceServiceId, testDeviceService, nil}, - {"UpdateDeviceService", adminStateUnlocked, testError, nil}}), - expectedError: true, - expectedErrorVal: testError, - }, - } - - for _, test := range tests { - t.Run(test.name, func(tt *testing.T) { - op := NewUpdateAdminStateByIdExecutor(testDeviceServiceId, testAdminState, test.mockUpdater, lc) - err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -func TestUpdateAdminStateByNameExecutor(t *testing.T) { - adminStateUnlocked := testDeviceService - adminStateUnlocked.AdminState = testAdminState - lc := logger.MockLogger{} - - tests := []struct { - name string - mockUpdater DeviceServiceUpdater - expectedError bool - expectedErrorVal error - }{ - { - name: "Successful database call", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetDeviceServiceByName", testDeviceServiceName, testDeviceService, nil}, - {"UpdateDeviceService", adminStateUnlocked, nil, nil}}), - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "Device service not found", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetDeviceServiceByName", testDeviceServiceName, contract.DeviceService{}, db.ErrNotFound}}), - expectedError: true, - expectedErrorVal: errors.NewErrItemNotFound(testDeviceServiceName), - }, - { - name: "Device service lookup error", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetDeviceServiceByName", testDeviceServiceName, contract.DeviceService{}, testError}}), - expectedError: true, - expectedErrorVal: testError, - }, - { - name: "Update error", - mockUpdater: createMockUpdater([]mockOutline{ - {"GetDeviceServiceByName", testDeviceServiceName, testDeviceService, nil}, - {"UpdateDeviceService", adminStateUnlocked, testError, nil}}), - expectedError: true, - expectedErrorVal: testError, - }, - } - - for _, test := range tests { - t.Run(test.name, func(tt *testing.T) { - op := NewUpdateAdminStateByNameExecutor(testDeviceServiceName, testAdminState, test.mockUpdater, lc) - err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -func createMockUpdater(outlines []mockOutline) DeviceServiceUpdater { - dbMock := mocks.DeviceServiceUpdater{} - - for _, o := range outlines { - dbMock.On(o.methodName, o.arg).Return(o.ret, o.err) - } - - return &dbMock -} - -var testAdminState, _ = contract.GetAdminState(contract.Unlocked) -var testOperatingState, _ = contract.GetOperatingState(contract.Enabled) diff --git a/internal/core/metadata/rest_addressable.go b/internal/core/metadata/rest_addressable.go deleted file mode 100644 index 831786d63f..0000000000 --- a/internal/core/metadata/rest_addressable.go +++ /dev/null @@ -1,337 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * - * 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 metadata - -import ( - "encoding/json" - "net/http" - "net/url" - "strconv" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/config" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/addressable" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" -) - -func restGetAllAddressables( - w http.ResponseWriter, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - op := addressable.NewAddressableLoadAll(configuration.Service, dbClient, lc) - addressables, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant( - w, err, - errorconcept.Common.LimitExceeded, - errorconcept.Default.InternalServerError) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - err = json.NewEncoder(w).Encode(&addressables) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } -} - -// Add a new addressable -// The name must be unique -func restAddAddressable( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - defer r.Body.Close() - var a models.Addressable - err := json.NewDecoder(r.Body).Decode(&a) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - op := addressable.NewAddExecutor(dbClient, a) - id, err := op.Execute() - if err != nil { - errorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.Common.DuplicateName, - errorconcept.Addressable.EmptyName, - }, - errorconcept.Default.InternalServerError) - return - } - w.WriteHeader(http.StatusOK) - _, err = w.Write([]byte(id)) - if err != nil { - lc.Error(err.Error()) - return - } -} - -// Update addressable by ID or name (ID used first) -func restUpdateAddressable( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - defer r.Body.Close() - var a models.Addressable - err := json.NewDecoder(r.Body).Decode(&a) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - op := addressable.NewUpdateExecutor(dbClient, a) - err = op.Execute() - if err != nil { - errorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.Addressable.NotFound, - errorconcept.Addressable.InUse, - }, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _, err = w.Write([]byte("true")) - if err != nil { - lc.Error(err.Error()) - return - } -} - -func restGetAddressableById( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var id = vars["id"] - op := addressable.NewIdExecutor(dbClient, id) - result, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.InternalServerError) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - json.NewEncoder(w).Encode(result) -} - -func restDeleteAddressableById( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var id = vars[ID] - - op := addressable.NewDeleteByIdExecutor(dbClient, id) - err := op.Execute() - if err != nil { - errorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.Addressable.NotFound, - errorconcept.Addressable.InUse, - }, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.Write([]byte("true")) -} - -func restDeleteAddressableByName( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - name, err := url.QueryUnescape(vars[NAME]) - // Problems unescaping - if err != nil { - errorHandler.Handle(w, err, errorconcept.Addressable.InvalidRequest_StatusInternalServer) - return - } - - op := addressable.NewDeleteByNameExecutor(dbClient, name) - err = op.Execute() - if err != nil { - errorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.Addressable.NotFound, - errorconcept.Addressable.InUse, - }, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restGetAddressableByName( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - dn, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusServiceUnavailable) - return - } - - op := addressable.NewNameExecutor(dbClient, dn) - result, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Database.NotFound, - errorconcept.Default.ServiceUnavailable) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - json.NewEncoder(w).Encode(result) -} - -func restGetAddressableByTopic( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - t, err := url.QueryUnescape(vars[TOPIC]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - op := addressable.NewTopicExecutor(dbClient, t) - res, err := op.Execute() - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusInternalServer) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - json.NewEncoder(w).Encode(res) -} - -func restGetAddressableByPort( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var strp = vars[PORT] - p, err := strconv.Atoi(strp) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - op := addressable.NewPortExecutor(dbClient, p) - res, err := op.Execute() - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusInternalServer) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - json.NewEncoder(w).Encode(res) -} - -func restGetAddressableByPublisher( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - p, err := url.QueryUnescape(vars[PUBLISHER]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - op := addressable.NewPublisherExecutor(dbClient, p) - res, err := op.Execute() - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusInternalServer) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - json.NewEncoder(w).Encode(res) -} - -func restGetAddressableByAddress( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - a, err := url.QueryUnescape(vars[ADDRESS]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - op := addressable.NewAddressExecutor(dbClient, a) - res, err := op.Execute() - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusInternalServer) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - json.NewEncoder(w).Encode(res) -} diff --git a/internal/core/metadata/rest_addressable_test.go b/internal/core/metadata/rest_addressable_test.go deleted file mode 100644 index b073e81718..0000000000 --- a/internal/core/metadata/rest_addressable_test.go +++ /dev/null @@ -1,791 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 metadata - -import ( - "bytes" - "encoding/json" - "errors" - "net/http" - "net/http/httptest" - "strconv" - "testing" - - metadataConfig "github.com/edgexfoundry/edgex-go/internal/core/metadata/config" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" - "github.com/stretchr/testify/mock" -) - -// AddressableTestURI this is not really used since we are using the HTTP testing framework and not creating routes, -// but rather creating a specific handler which will accept all requests. Therefore, the URI is not important. -var AddressableTestURI = "/addressable" -var TestAddress = "TestAddress" -var TestPort = 8080 -var TestPublisher = "TestPublisher" -var TestTopic = "TestTopic" -var TestName = "AddressableName" -var TestId = "123e4567-e89b-12d3-a456-426655440000" - -// ErrorPathParam path parameter value which will trigger the 'mux.Vars' function to throw an error due to the '%' not -// being followed by a valid hexadecimal number. -var ErrorPathParam = "%zz" - -// ErrorPortPathParam path parameter used to trigger an error in the `restGetAddressableByPort` function where the -// port variable is expected to be a number. -var ErrorPortPathParam = "abc" - -func TestGetAllAddressables(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createAddressableRequest(http.MethodGet, "", ""), - createMockAddressLoader(5, nil), - http.StatusOK, - }, - { - "OK(No Addressables)", - createAddressableRequest(http.MethodGet, "", ""), - createMockAddressLoader(0, nil), - http.StatusOK, - }, - { - "Error Limit Exceeded", - createAddressableRequest(http.MethodGet, "", ""), - createMockAddressLoader(11, nil), - http.StatusRequestEntityTooLarge, - }, - { - "Error Unknown", - createAddressableRequest(http.MethodGet, "", ""), - createMockAddressLoader(0, errors.New("Some error")), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - configuration := metadataConfig.ConfigurationStruct{ - Service: bootstrapConfig.ServiceInfo{MaxResultCount: 10}, - } - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - restGetAllAddressables( - rr, - loggerMock, - tt.dbMock, - errorconcept.NewErrorHandler(loggerMock), - &configuration) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestAddAddressable(t *testing.T) { - noName := createAddressables(1)[0] - noName.Name = "" - - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "OK", - request: createAddressableRequestWithBody(http.MethodPost, createAddressables(1)[0], ID, TestId), - dbMock: createMockWithOutlines([]mockOutline{ - {"AddAddressable", []interface{}{mock.Anything}, []interface{}{TestId, nil}}, - }), - expectedStatus: http.StatusOK, - }, - { - name: "Missing name field on addressable", - request: createAddressableRequestWithBody(http.MethodPost, noName, ID, TestId), - dbMock: createMockWithOutlines([]mockOutline{ - {"AddAddressable", []interface{}{mock.Anything}, []interface{}{TestId, nil}}, - }), - expectedStatus: http.StatusBadRequest, - }, - { - name: "Duplicated addressable", - request: createAddressableRequestWithBody(http.MethodPost, createAddressables(1)[0], ID, TestId), - dbMock: createMockWithOutlines([]mockOutline{ - {"AddAddressable", []interface{}{mock.Anything}, []interface{}{"", db.ErrNotUnique}}, - }), - expectedStatus: http.StatusConflict, - }, - { - name: "Other error from database", - request: createAddressableRequestWithBody(http.MethodPost, createAddressables(1)[0], ID, TestId), - dbMock: createMockWithOutlines([]mockOutline{ - {"AddAddressable", []interface{}{mock.Anything}, []interface{}{"", errors.New("some error")}}, - }), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Bad JSON parse", - request: createAddressableRequest(http.MethodPost, ID, TestId), - dbMock: nil, - expectedStatus: http.StatusBadRequest, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - restAddAddressable( - rr, - tt.request, - loggerMock, - tt.dbMock, errorconcept.NewErrorHandler(loggerMock)) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestUpdateAddressable(t *testing.T) { - successNewName := createAddressables(1)[0] - successNewName.Name = "something different" - - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "OK", - request: createAddressableRequestWithBody(http.MethodPut, createAddressables(1)[0], ID, TestId), - dbMock: createMockWithOutlines([]mockOutline{ - {"GetAddressableById", []interface{}{createAddressables(1)[0].Id}, []interface{}{createAddressables(1)[0], nil}}, - {"UpdateAddressable", []interface{}{mock.Anything}, []interface{}{nil, nil}}, - }), - expectedStatus: http.StatusOK, - }, - { - name: "Unsuccessful database call", - request: createAddressableRequestWithBody(http.MethodPut, createAddressables(1)[0], ID, TestId), - dbMock: createMockWithOutlines([]mockOutline{ - {"GetAddressableById", []interface{}{createAddressables(1)[0].Id}, []interface{}{createAddressables(1)[0], nil}}, - {"UpdateAddressable", []interface{}{mock.Anything}, []interface{}{errors.New("some error"), nil}}, - }), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Unsuccessful device service database call, updated name", - request: createAddressableRequestWithBody(http.MethodPut, successNewName, ID, TestId), - dbMock: createMockWithOutlines([]mockOutline{ - {"GetAddressableById", []interface{}{createAddressables(1)[0].Id}, []interface{}{createAddressables(1)[0], nil}}, - {"UpdateAddressable", []interface{}{mock.Anything}, []interface{}{nil, nil}}, - {"GetDeviceServicesByAddressableId", []interface{}{mock.Anything}, []interface{}{nil, errors.New("some error")}}, - }), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Addressable in use", - request: createAddressableRequestWithBody(http.MethodPut, successNewName, ID, TestId), - dbMock: createMockWithOutlines([]mockOutline{ - {"GetAddressableById", []interface{}{createAddressables(1)[0].Id}, []interface{}{createAddressables(1)[0], nil}}, - {"UpdateAddressable", []interface{}{mock.Anything}, []interface{}{nil, nil}}, - {"GetDeviceServicesByAddressableId", []interface{}{mock.Anything}, []interface{}{[]contract.DeviceService{{}}, nil}}, - }), - expectedStatus: http.StatusConflict, - }, - { - name: "Addressable not found", - request: createAddressableRequestWithBody(http.MethodPut, createAddressables(1)[0], ID, TestId), - dbMock: createMockWithOutlines([]mockOutline{ - {"GetAddressableById", []interface{}{createAddressables(1)[0].Id}, []interface{}{contract.Addressable{}, db.ErrNotFound}}, - }), - expectedStatus: http.StatusNotFound, - }, - { - name: "Bad JSON parse", - request: createAddressableRequest(http.MethodPut, ID, TestId), - dbMock: nil, - expectedStatus: http.StatusBadRequest, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - restUpdateAddressable( - rr, - tt.request, - loggerMock, - tt.dbMock, - errorconcept.NewErrorHandler(loggerMock)) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestGetAddressableByName(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createAddressableRequest(http.MethodGet, NAME, TestName), - createMockAddressLoaderForName(nil), - http.StatusOK, - }, - { - name: "Bad escape character", - request: createAddressableRequest(http.MethodGet, NAME, TestName+"%zz"), - dbMock: createMockAddressLoaderForName(nil), - expectedStatus: http.StatusServiceUnavailable, - }, - { - name: "Addressable not found", - request: createAddressableRequest(http.MethodGet, NAME, TestName), - dbMock: createMockAddressLoaderForName(db.ErrNotFound), - expectedStatus: http.StatusNotFound, - }, - { - name: "Other error from database", - request: createAddressableRequest(http.MethodGet, NAME, TestName), - dbMock: createMockAddressLoaderForName(errors.New("Test error")), - expectedStatus: http.StatusServiceUnavailable, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetAddressableByName(rr, tt.request, tt.dbMock, errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestGetAddressableById(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createAddressableRequest(http.MethodGet, ID, TestId), - createMockAddressLoaderForId(nil), - http.StatusOK, - }, - { - name: "Addressable not found", - request: createAddressableRequest(http.MethodGet, ID, TestId), - dbMock: createMockAddressLoaderForId(db.ErrNotFound), - expectedStatus: http.StatusNotFound, - }, - { - name: "Other error from database", - request: createAddressableRequest(http.MethodGet, ID, TestId), - dbMock: createMockAddressLoaderForId(errors.New("Test error")), - expectedStatus: http.StatusInternalServerError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetAddressableById(rr, tt.request, tt.dbMock, errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestGetAddressablesByAddress(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createAddressableRequest(http.MethodGet, ADDRESS, TestAddress), - createMockAddressLoaderStringArg(1, "GetAddressablesByAddress", TestAddress), - http.StatusOK, - }, - { - "OK(Multiple matches)", - createAddressableRequest(http.MethodGet, ADDRESS, TestAddress), - createMockAddressLoaderStringArg(3, "GetAddressablesByAddress", TestAddress), - http.StatusOK, - }, - { - - "OK(No matches)", - createAddressableRequest(http.MethodGet, ADDRESS, TestAddress), - createMockAddressLoaderStringArg(0, "GetAddressablesByAddress", TestAddress), - http.StatusOK, - }, - { - "Invalid ADDRESS path parameter", - createAddressableRequest(http.MethodGet, ADDRESS, ErrorPathParam), - createMockAddressLoaderStringArg(1, "GetAddressablesByAddress", TestAddress), - http.StatusBadRequest, - }, - { - "Internal Server Error", - createAddressableRequest(http.MethodGet, ADDRESS, TestAddress), - createErrorMockAddressLoaderStringArg("GetAddressablesByAddress", TestAddress), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetAddressableByAddress(rr, tt.request, tt.dbMock, errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestGetAddressablesByPublisher(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - {"OK", - createAddressableRequest(http.MethodGet, PUBLISHER, TestPublisher), - createMockAddressLoaderStringArg(1, "GetAddressablesByPublisher", TestPublisher), - http.StatusOK, - }, - { - "OK(Multiple matches)", - createAddressableRequest(http.MethodGet, PUBLISHER, TestPublisher), createMockAddressLoaderStringArg(3, "GetAddressablesByPublisher", TestPublisher), - http.StatusOK, - }, - { - "OK(No matches)", - createAddressableRequest(http.MethodGet, PUBLISHER, TestPublisher), - createMockAddressLoaderStringArg(0, "GetAddressablesByPublisher", TestPublisher), - http.StatusOK, - }, - { - "Invalid PUBLISHER path parameter", - createAddressableRequest(http.MethodGet, PUBLISHER, ErrorPathParam), - createMockAddressLoaderStringArg(1, "GetAddressablesByPublisher", TestPublisher), - http.StatusBadRequest, - }, - { - "Internal Server Error", - createAddressableRequest(http.MethodGet, PUBLISHER, TestPublisher), - createErrorMockAddressLoaderStringArg("GetAddressablesByPublisher", TestPublisher), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetAddressableByPublisher( - rr, - tt.request, - tt.dbMock, - errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestGetAddressablesByPort(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createAddressableRequest(http.MethodGet, PORT, strconv.Itoa(TestPort)), - createMockAddressLoaderForPort(1, "GetAddressablesByPort"), - http.StatusOK, - }, - { - "OK(Multiple matches)", - createAddressableRequest(http.MethodGet, PORT, strconv.Itoa(TestPort)), - createMockAddressLoaderForPort(3, "GetAddressablesByPort"), - http.StatusOK, - }, - { - "OK(No matches)", - createAddressableRequest(http.MethodGet, PORT, strconv.Itoa(TestPort)), - createMockAddressLoaderForPort(0, "GetAddressablesByPort"), - http.StatusOK, - }, - { - "Invalid PORT path parameter", - createAddressableRequest(http.MethodGet, PORT, ErrorPathParam), - createMockAddressLoaderForPort(1, "GetAddressablesByPort"), - http.StatusBadRequest, - }, - { - "Non-integer PORT path parameter", - createAddressableRequest(http.MethodGet, PORT, ErrorPortPathParam), - createMockAddressLoaderForPort(1, "GetAddressablesByPort"), - http.StatusBadRequest, - }, - { - "Internal Server Error", - createAddressableRequest(http.MethodGet, PORT, strconv.Itoa(TestPort)), - createErrorMockAddressLoaderPortExecutor("GetAddressablesByPort"), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetAddressableByPort(rr, tt.request, tt.dbMock, errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestGetAddressablesByTopic(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createAddressableRequest(http.MethodGet, TOPIC, TestTopic), - createMockAddressLoaderStringArg(1, "GetAddressablesByTopic", TestTopic), - http.StatusOK, - }, - { - "OK(Multiple matches)", - createAddressableRequest(http.MethodGet, TOPIC, TestTopic), - createMockAddressLoaderStringArg(3, "GetAddressablesByTopic", TestTopic), - http.StatusOK, - }, - { - "OK(No matches)", - createAddressableRequest(http.MethodGet, TOPIC, TestTopic), - createMockAddressLoaderStringArg(0, "GetAddressablesByTopic", TestTopic), - http.StatusOK, - }, - { - "Invalid TOPIC path parameter", - createAddressableRequest(http.MethodGet, TOPIC, ErrorPathParam), - createMockAddressLoaderStringArg(1, "GetAddressablesByTopic", TestTopic), - http.StatusBadRequest, - }, - { - "Internal Server Error", - createAddressableRequest(http.MethodGet, TOPIC, TestTopic), - createErrorMockAddressLoaderStringArg("GetAddressablesByTopic", TestTopic), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetAddressableByTopic(rr, tt.request, tt.dbMock, errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestDeleteAddressableById(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createAddressableRequest(http.MethodDelete, ID, TestId), - createMockWithOutlines([]mockOutline{ - {"GetAddressableById", []interface{}{TestId}, []interface{}{createAddressables(1)[0], nil}}, - {"GetAddressableByName", []interface{}{mock.Anything}, []interface{}{contract.Addressable{}, errors.New("some error")}}, - {"GetDeviceServicesByAddressableId", []interface{}{mock.Anything}, []interface{}{[]contract.DeviceService{}, nil}}, - {"DeleteAddressableById", []interface{}{mock.Anything}, []interface{}{nil, nil}}}), - http.StatusOK, - }, - { - name: "Addressable not found", - request: createAddressableRequest(http.MethodDelete, ID, TestId), - dbMock: createMockWithOutlines([]mockOutline{ - {"GetAddressableByName", []interface{}{TestId}, []interface{}{contract.Addressable{}, db.ErrNotFound}}, - {"GetAddressableById", []interface{}{TestId}, []interface{}{contract.Addressable{}, db.ErrNotFound}}, - }), - expectedStatus: http.StatusNotFound, - }, - { - name: "Addressable in use", - request: createAddressableRequest(http.MethodDelete, ID, TestId), - dbMock: createMockWithOutlines([]mockOutline{ - {"GetAddressableById", []interface{}{TestId}, []interface{}{createAddressables(1)[0], nil}}, - {"GetAddressableByName", []interface{}{mock.Anything}, []interface{}{contract.Addressable{}, errors.New("some error")}}, - {"GetDeviceServicesByAddressableId", []interface{}{mock.Anything}, []interface{}{[]contract.DeviceService{{}}, nil}}, - {"DeleteAddressableById", []interface{}{mock.Anything}, []interface{}{nil, nil}}, - }), - expectedStatus: http.StatusConflict, - }, - { - name: "Other error from database", - request: createAddressableRequest(http.MethodDelete, ID, TestId), - dbMock: createMockWithOutlines([]mockOutline{ - {"GetAddressableByName", []interface{}{TestId}, []interface{}{contract.Addressable{}, errors.New("some error")}}, - {"GetAddressableById", []interface{}{TestId}, []interface{}{contract.Addressable{}, errors.New("some error")}}, - }), - expectedStatus: http.StatusInternalServerError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restDeleteAddressableById(rr, tt.request, tt.dbMock, errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestDeleteAddressableByName(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createAddressableRequest(http.MethodDelete, NAME, TestName), - createMockWithOutlines([]mockOutline{ - {"GetAddressableByName", []interface{}{TestName}, []interface{}{createAddressables(1)[0], nil}}, - {"GetAddressableById", []interface{}{mock.Anything}, []interface{}{contract.Addressable{}, errors.New("some error")}}, - {"GetDeviceServicesByAddressableId", []interface{}{mock.Anything}, []interface{}{[]contract.DeviceService{}, nil}}, - {"DeleteAddressableById", []interface{}{mock.Anything}, []interface{}{nil, nil}}, - }), - http.StatusOK, - }, - { - name: "Addressable not found", - request: createAddressableRequest(http.MethodDelete, NAME, TestName), - dbMock: createMockWithOutlines([]mockOutline{ - {"GetAddressableByName", []interface{}{TestName}, []interface{}{contract.Addressable{}, db.ErrNotFound}}, - {"GetAddressableById", []interface{}{TestName}, []interface{}{contract.Addressable{}, db.ErrNotFound}}, - }), - expectedStatus: http.StatusNotFound, - }, - { - name: "Addressable in use", - request: createAddressableRequest(http.MethodDelete, NAME, TestName), - dbMock: createMockWithOutlines([]mockOutline{ - {"GetAddressableByName", []interface{}{TestName}, []interface{}{createAddressables(1)[0], nil}}, - {"GetAddressableById", []interface{}{mock.Anything}, []interface{}{contract.Addressable{}, errors.New("some error")}}, - {"GetDeviceServicesByAddressableId", []interface{}{mock.Anything}, []interface{}{[]contract.DeviceService{{}}, nil}}, - {"DeleteAddressableById", []interface{}{mock.Anything}, []interface{}{nil, nil}}, - }), - expectedStatus: http.StatusConflict, - }, - { - name: "Other error from database", - request: createAddressableRequest(http.MethodDelete, NAME, TestName), - dbMock: createMockWithOutlines([]mockOutline{ - {"GetAddressableByName", []interface{}{TestName}, []interface{}{contract.Addressable{}, errors.New("some error")}}, - {"GetAddressableById", []interface{}{TestName}, []interface{}{contract.Addressable{}, errors.New("some error")}}, - }), - expectedStatus: http.StatusInternalServerError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restDeleteAddressableByName(rr, tt.request, tt.dbMock, errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func createAddressableRequest(httpMethod string, pathParamName string, pathParamValue string) *http.Request { - req := httptest.NewRequest(httpMethod, AddressableTestURI, nil) - return mux.SetURLVars(req, map[string]string{pathParamName: pathParamValue}) -} - -func createAddressableRequestWithBody( - httpMethod string, - addressable contract.Addressable, - pathParamName string, - pathParamValue string) *http.Request { - - // if your JSON marshalling fails you've got bigger problems - body, _ := json.Marshal(addressable) - - req := httptest.NewRequest(httpMethod, AddressableTestURI, bytes.NewReader(body)) - - return mux.SetURLVars(req, map[string]string{pathParamName: pathParamValue}) -} - -func createAddressables(howMany int) []contract.Addressable { - var addressables []contract.Addressable - for i := 0; i < howMany; i++ { - addressables = append(addressables, contract.Addressable{ - Name: "Name" + strconv.Itoa(i), - User: "User" + strconv.Itoa(i), - Protocol: "http", - Id: "address" + strconv.Itoa(i), - HTTPMethod: "POST", - }) - } - return addressables -} - -func createMockAddressLoaderStringArg(howMany int, methodName string, arg string) interfaces.DBClient { - addressables := createAddressables(howMany) - - myMock := mocks.DBClient{} - myMock.On(methodName, arg).Return(addressables, nil) - return &myMock -} - -func createMockAddressLoaderForPort(howMany int, methodName string) interfaces.DBClient { - addressables := createAddressables(howMany) - - myMock := mocks.DBClient{} - myMock.On(methodName, TestPort).Return(addressables, nil) - return &myMock -} - -func createMockAddressLoaderForName(desiredError error) interfaces.DBClient { - myMock := mocks.DBClient{} - - if desiredError != nil { - myMock.On("GetAddressableByName", TestName).Return(contract.Addressable{}, desiredError) - } else { - myMock.On("GetAddressableByName", TestName).Return(createAddressables(1)[0], nil) - } - return &myMock -} - -func createMockAddressLoaderForId(desiredError error) interfaces.DBClient { - myMock := mocks.DBClient{} - - if desiredError != nil { - myMock.On("GetAddressableById", TestId).Return(contract.Addressable{}, desiredError) - } else { - myMock.On("GetAddressableById", TestId).Return(createAddressables(1)[0], nil) - } - return &myMock -} - -func createErrorMockAddressLoaderStringArg(methodName string, arg string) interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On(methodName, arg).Return(nil, errors.New("test error")) - return &myMock -} - -func createErrorMockAddressLoaderPortExecutor(methodName string) interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On(methodName, TestPort).Return(nil, errors.New("test error")) - return &myMock -} - -func createMockAddressLoader(howMany int, err error) interfaces.DBClient { - addressables := createAddressables(howMany) - - dbMock := mocks.DBClient{} - dbMock.On("GetAddressables").Return(addressables, err) - return &dbMock -} - -type mockOutline struct { - methodName string - arg []interface{} - ret []interface{} -} - -func createMockWithOutlines(outlines []mockOutline) interfaces.DBClient { - dbMock := mocks.DBClient{} - - for _, o := range outlines { - dbMock.On(o.methodName, o.arg...).Return(o.ret...) - } - - return &dbMock -} diff --git a/internal/core/metadata/rest_command.go b/internal/core/metadata/rest_command.go deleted file mode 100644 index 124abc1dc2..0000000000 --- a/internal/core/metadata/rest_command.go +++ /dev/null @@ -1,126 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * - * 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 metadata - -import ( - "net/http" - "net/url" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/config" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/command" - "github.com/edgexfoundry/edgex-go/internal/pkg" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - - "github.com/gorilla/mux" -) - -func restGetAllCommands( - w http.ResponseWriter, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - op := command.NewCommandLoadAll(configuration.Service, dbClient) - cmds, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.LimitExceeded, - errorconcept.Default.InternalServerError) - return - } - pkg.Encode(&cmds, w, lc) -} - -func restGetCommandById( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - cid, err := url.QueryUnescape(vars[ID]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - op := command.NewCommandById(dbClient, cid) - cmd, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.ItemNotFound, - errorconcept.Default.InternalServerError) - return - } - pkg.Encode(cmd, w, lc) -} - -func restGetCommandsByName( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - op := command.NewCommandsByName(dbClient, n) - cmds, err := op.Execute() - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusInternalServer) - return - } - pkg.Encode(&cmds, w, lc) -} - -func restGetCommandsByDeviceId( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - did, err := url.QueryUnescape(vars[ID]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - op := command.NewDeviceIdExecutor(dbClient, did) - commands, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.ItemNotFound, - errorconcept.Default.InternalServerError) - return - } - pkg.Encode(&commands, w, lc) -} diff --git a/internal/core/metadata/rest_command_test.go b/internal/core/metadata/rest_command_test.go deleted file mode 100644 index f32adb885f..0000000000 --- a/internal/core/metadata/rest_command_test.go +++ /dev/null @@ -1,268 +0,0 @@ -package metadata - -import ( - "fmt" - "net/http" - "net/http/httptest" - "testing" - - metadataConfig "github.com/edgexfoundry/edgex-go/internal/core/metadata/config" - types "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" - "github.com/pkg/errors" -) - -func TestGetCommandsByDeviceId(t *testing.T) { - tests := []struct { - name string - dbMock interfaces.DBClient - request *http.Request - expectedStatus int - }{ - { - "OK", - createCommandByDeviceIdLoaderMock(), - createCommandRequest(cmdsByDeviceIdURL, ID, deviceId), - http.StatusOK, - }, - { - "Unexpected", - createMockCommandLoaderMock("GetCommandsByDeviceId", deviceId, nil, unExpectedError), - createCommandRequest(cmdsByDeviceIdURL, ID, deviceId), - http.StatusInternalServerError, - }, - { - "NotFound", - createMockCommandLoaderMock("GetCommandsByDeviceId", deviceId, nil, deviceNotFoundErr), - createCommandRequest(cmdsByDeviceIdURL, ID, deviceId), - http.StatusNotFound, - }, - { - "BadRequest", - createMockCommandLoaderMock("GetCommandsByDeviceId", deviceId, nil, nil), - createCommandRequest(cmdsByDeviceIdURL, ID, ErrorPathParam), - http.StatusBadRequest, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - restGetCommandsByDeviceId( - rr, - tt.request, - loggerMock, - tt.dbMock, - errorconcept.NewErrorHandler(loggerMock)) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestGetAllCommands(t *testing.T) { - tests := []struct { - name string - dbMock interfaces.DBClient - request *http.Request - expectedStatus int - }{ - {"OK", createCommandsLoaderMock(1), createPlainCommandRequest(cmdsURL), http.StatusOK}, - {"Unexpected", createCommandsLoaderMockUnexpectedFail(), createPlainCommandRequest(cmdsURL), http.StatusInternalServerError}, - {"MaxExceeded", createCommandsLoaderMock(3), createPlainCommandRequest(cmdsURL), http.StatusRequestEntityTooLarge}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - configuration := metadataConfig.ConfigurationStruct{ - Service: bootstrapConfig.ServiceInfo{MaxResultCount: 1}, - } - restGetAllCommands( - rr, - loggerMock, - tt.dbMock, - errorconcept.NewErrorHandler(loggerMock), - &configuration) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestGetCommandById(t *testing.T) { - tests := []struct { - name string - dbMock interfaces.DBClient - request *http.Request - expectedStatus int - }{ - - { - "OK", - createMockCommandLoaderMock("GetCommandById", commandId, contract.Command{Name: fmt.Sprintf("CommandName"), Id: commandId}, nil), - createCommandRequest(cmdByIdURL, ID, commandId), - http.StatusOK, - }, - { - "Unexpected", - createMockCommandLoaderMock("GetCommandById", commandId, contract.Command{}, unExpectedError), - createCommandRequest(cmdByIdURL, ID, commandId), - http.StatusInternalServerError, - }, - { - "NotFound", - createMockCommandLoaderMock("GetCommandById", commandId, contract.Command{}, cmdNotFoundErr), - createCommandRequest(cmdByIdURL, ID, commandId), - http.StatusNotFound, - }, - { - "BadRequest", - createMockCommandLoaderMock("GetCommandById", commandId, contract.Command{}, nil), - createCommandRequest(cmdByIdURL, ID, ErrorPathParam), - http.StatusBadRequest, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - restGetCommandById( - rr, - tt.request, - loggerMock, - tt.dbMock, - errorconcept.NewErrorHandler(loggerMock)) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestGetCommandsByName(t *testing.T) { - tests := []struct { - name string - dbMock interfaces.DBClient - request *http.Request - expectedStatus int - }{ - - { - "OK", - createMockCommandLoaderMock("GetCommandsByName", commandName, []contract.Command{{Name: commandName}}, nil), - createCommandRequest(cmdByNameURL, NAME, commandName), - http.StatusOK, - }, - { - "Unexpected", - createMockCommandLoaderMock("GetCommandsByName", commandName, nil, unExpectedError), - createCommandRequest(cmdByNameURL, NAME, commandName), - http.StatusInternalServerError, - }, - { - "BadRequest", - createMockCommandLoaderMock("GetCommandsByName", commandName, nil, nil), - createCommandRequest(cmdByNameURL, NAME, ErrorPathParam), - http.StatusBadRequest, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - restGetCommandsByName( - rr, - tt.request, - loggerMock, - tt.dbMock, - errorconcept.NewErrorHandler(loggerMock)) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -//should be common constants -var cmdsURL = clients.ApiBase + "/" + COMMAND -var cmdByIdURL = cmdsURL + "/{" + ID + "}" -var cmdByNameURL = cmdsURL + "/" + NAME + "/{" + NAME + "}" -var cmdsByDeviceIdURL = cmdsURL + "/" + DEVICE + "/{" + ID + "}" - -var commandName = "Command 0" -var commandId = "f97b5f0a-ec32-4e96-bd36-02210af16f8c" -var deviceId = "b3445cc6-87df-48f4-b8b0-587dc8a4e1c2" - -var unExpectedError = errors.New("unexpected error") -var cmdNotFoundErr = types.NewErrItemNotFound(fmt.Sprintf("command with id %s not found", commandId)) -var deviceNotFoundErr = types.NewErrItemNotFound(fmt.Sprintf("device with id %s not found", deviceId)) - -var commands = []contract.Command{ - {Name: fmt.Sprintf(commandName)}, - {Name: fmt.Sprintf("Command 1")}, - {Name: fmt.Sprintf("Command 2")}, -} - -func createPlainCommandRequest(url string) *http.Request { - - return createCommandRequest(url, "", "") -} - -func createCommandRequest(url string, pathParamName string, pathParamValue string) *http.Request { - req := httptest.NewRequest(http.MethodGet, url, nil) - if pathParamName == "" && pathParamValue == "" { - return req - } - return mux.SetURLVars(req, map[string]string{pathParamName: pathParamValue}) -} - -func createCommandByDeviceIdLoaderMock() interfaces.DBClient { - dbMock := &mocks.DBClient{} - dbMock.On("GetCommandsByDeviceId", deviceId).Return(commands, nil) - return dbMock -} - -//TestGetAllCommands Mocks -func createCommandsLoaderMock(howMany int) interfaces.DBClient { - commands := []contract.Command{} - for i := 0; i < howMany; i++ { - commands = append(commands, contract.Command{Name: fmt.Sprintf("Command %v", i)}) - } - - dbMock := &mocks.DBClient{} - dbMock.On("GetAllCommands").Return(commands, nil) - return dbMock -} - -func createCommandsLoaderMockUnexpectedFail() interfaces.DBClient { - dbMock := &mocks.DBClient{} - dbMock.On("GetAllCommands").Return(nil, errors.New("unexpected error")) - return dbMock -} - -func createMockCommandLoaderMock(methodName string, arg string, result interface{}, err error) interfaces.DBClient { - myMock := &mocks.DBClient{} - myMock.On(methodName, arg).Return(result, err) - return myMock -} diff --git a/internal/core/metadata/rest_device.go b/internal/core/metadata/rest_device.go deleted file mode 100644 index 579741b086..0000000000 --- a/internal/core/metadata/rest_device.go +++ /dev/null @@ -1,1088 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * - * 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 metadata - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "strconv" - "strings" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/config" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/device" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/notifications" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/edgexfoundry/go-mod-core-contracts/v2/requests/states/admin" - "github.com/edgexfoundry/go-mod-core-contracts/v2/requests/states/operating" - - "github.com/gorilla/mux" -) - -func restGetAllDevices( - w http.ResponseWriter, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - op := device.NewDeviceLoadAll(configuration.Service, dbClient, lc) - devices, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.LimitExceeded, - errorconcept.Default.InternalServerError) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(&devices) -} - -// Post a new device -// Attached objects (Addressable, Profile, Service) are referenced by ID or name -// 409 conflict if any of the attached items can't be found by ID or name -// Ignore everything else from the attached objects -func restAddNewDevice( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) { - - defer r.Body.Close() - - var d models.Device - err := json.NewDecoder(r.Body).Decode(&d) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.ContractInvalid_StatusBadRequest, - errorconcept.Default.InternalServerError) - return - } - - ctx := r.Context() - // The following requester instance is necessary because we will be making an HTTP call to the device service - // associated with the new device in the Notifier below. There is no device service client. Additionally, the - // requester interface should be mocked for unit testability and so is injected into the Notifier. - requester, err := device.NewRequester(device.Http, lc, ctx) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Device.RequesterError) - return - } - - ch := make(chan device.DeviceEvent) - defer close(ch) - - notifier := device.NewNotifier(ch, nc, configuration.Notifications, dbClient, requester, lc, ctx) - go notifier.Execute() - - op := device.NewAddDevice(ch, dbClient, d) - newId, err := op.Execute() - if err != nil { - errorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.Common.DuplicateName, - errorconcept.Common.ItemNotFound, - }, - errorconcept.Default.InternalServerError) - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte(newId)) -} - -// Update the device -// Use ID to identify device first, then name -// Can't create new Device Services/Profiles with a PUT, but you can reference another one -func restUpdateDevice( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) { - - defer r.Body.Close() - var rd models.Device - err := json.NewDecoder(r.Body).Decode(&rd) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusServiceUnavailable) - return - } - - ch := make(chan device.DeviceEvent) - defer close(ch) - - ctx := r.Context() - - requester, err := device.NewRequester(device.Http, lc, ctx) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Device.RequesterError) - return - } - - notifier := device.NewNotifier(ch, nc, configuration.Notifications, dbClient, requester, lc, ctx) - go notifier.Execute() - - op := device.NewUpdateDevice(ch, dbClient, rd, lc) - err = op.Execute() - - if err != nil { - errorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.Common.DuplicateName, - errorconcept.Common.ItemNotFound, - }, - errorconcept.Default.InternalServerError) - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restGetDevicesWithLabel( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - label, err := url.QueryUnescape(vars[LABEL]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - res, err := dbClient.GetDevicesWithLabel(label) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusInternalServer) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -func restGetDeviceByProfileId( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var pid = vars[PROFILEID] - - // Check if the device profile exists - _, err := dbClient.GetDeviceProfileById(pid) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.InternalServerError) - return - } - - res, err := dbClient.GetDevicesByProfileId(pid) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusInternalServer) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -func restGetDeviceByServiceId( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var sid = vars[SERVICEID] - - // Check if the device service exists - _, err := dbClient.GetDeviceServiceById(sid) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.ServiceUnavailable) - return - } - - res, err := dbClient.GetDevicesByServiceId(sid) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusServiceUnavailable) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -// If the result array is empty, don't return http.NotFound, just return empty array -func restGetDeviceByServiceName( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - sn, err := url.QueryUnescape(vars[SERVICENAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusServiceUnavailable) - return - } - - // Check if the device service exists - ds, err := dbClient.GetDeviceServiceByName(sn) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.ServiceUnavailable) - return - } - - // Find devices by service ID now that you have the Service object (and therefor the ID) - res, err := dbClient.GetDevicesByServiceId(ds.Id) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusServiceUnavailable) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -func restGetDeviceByProfileName( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - pn, err := url.QueryUnescape(vars[PROFILENAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusServiceUnavailable) - return - } - - // Check if the device profile exists - dp, err := dbClient.GetDeviceProfileByName(pn) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.ServiceUnavailable) - return - } - - // Use profile ID now that you have the profile object - res, err := dbClient.GetDevicesByProfileId(dp.Id) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusServiceUnavailable) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -func restGetDeviceById( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var did = vars[ID] - - res, err := dbClient.GetDeviceById(did) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.BadRequest) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -// restCheckForDevice looks for a device using both its name and device, in that order. If found, -// it is returned as a JSON encoded string. -func restCheckForDevice( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - token := vars[ID] // referring to this as "token" for now since the source variable is double purposed - - // Check for name first since we're using that meaning by default. - dev, err := dbClient.GetDeviceByName(token) - if err != nil { - if err != db.ErrNotFound { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusInternalServer) - return - } else { - lc.Debug(fmt.Sprintf("device %s %v", token, err)) - } - } - - // If lookup by name failed, see if we were passed the ID - if len(dev.Name) == 0 { - if dev, err = dbClient.GetDeviceById(token); err != nil { - errorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.Database.NotFound, - errorconcept.Database.InvalidObjectId, - }, - errorconcept.Default.InternalServerError) - return - } - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(dev) -} - -func decodeState(r *http.Request) (mode string, state string, err error) { - var adminReq admin.UpdateRequest - var opsReq operating.UpdateRequest - - bodyBytes, err := ioutil.ReadAll(r.Body) - if err != nil { - return - } - - var errMsg string - decoder := json.NewDecoder(bytes.NewBuffer(bodyBytes)) - err = decoder.Decode(&adminReq) - if err != nil { - switch err := err.(type) { - case models.ErrContractInvalid: - errMsg = err.Error() - default: - return "", "", err - } - } else { - return ADMINSTATE, string(adminReq.AdminState), nil - } - - // In this case, the supplied request was not for the AdminState. Try OperatingState. - decoder = json.NewDecoder(bytes.NewBuffer(bodyBytes)) - err = decoder.Decode(&opsReq) - if err != nil { - switch err := err.(type) { - case models.ErrContractInvalid: - errMsg += "; " + err.Error() - default: - return "", "", err - } - } else { - return OPSTATE, string(opsReq.OperatingState), nil - } - - // In this case, the request we were given in completely invalid - return "", "", fmt.Errorf( - "unknown request type: data decode failed for both states: %v", - errMsg) -} - -func restSetDeviceStateById( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) { - - vars := mux.Vars(r) - var did = vars[ID] - updateMode, state, err := decodeState(r) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the device exists - d, err := dbClient.GetDeviceById(did) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.ServiceUnavailable) - return - } - - if err = updateDeviceState(updateMode, state, d, dbClient); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.UpdateError_StatusInternalServer) - return - } - - // Notify - _ = notifyDeviceAssociates(d, http.MethodPut, r.Context(), lc, dbClient, nc, configuration) - - w.WriteHeader(http.StatusOK) -} - -func restSetDeviceStateByDeviceName( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - updateMode, state, err := decodeState(r) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the device exists - d, err := dbClient.GetDeviceByName(n) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.InternalServerError) - return - } - - if err = updateDeviceState(updateMode, state, d, dbClient); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.UpdateError_StatusInternalServer) - return - } - - ctx := r.Context() - // Notify - _ = notifyDeviceAssociates(d, http.MethodPut, ctx, lc, dbClient, nc, configuration) - - w.WriteHeader(http.StatusOK) -} - -func updateDeviceState(updateMode string, state string, d models.Device, dbClient interfaces.DBClient) error { - switch updateMode { - case ADMINSTATE: - d.AdminState = models.AdminState(strings.ToUpper(state)) - case OPSTATE: - d.OperatingState = models.OperatingState(strings.ToUpper(state)) - } - return dbClient.UpdateDevice(d) -} - -func restDeleteDeviceById( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) { - - vars := mux.Vars(r) - var did = vars[ID] - - // Check if the device exists - d, err := dbClient.GetDeviceById(did) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.BadRequest) - return - } - - ctx := r.Context() - if err := deleteDevice(d, w, ctx, lc, dbClient, errorHandler, nc, configuration); err != nil { - lc.Error(err.Error()) - return - } - - w.Write([]byte("true")) -} - -func restDeleteDeviceByName( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the device exists - d, err := dbClient.GetDeviceByName(n) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Device.NotFound) - return - } - - ctx := r.Context() - if err := deleteDevice(d, w, ctx, lc, dbClient, errorHandler, nc, configuration); err != nil { - lc.Error(err.Error()) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -// Delete the device -func deleteDevice( - d models.Device, - w http.ResponseWriter, - ctx context.Context, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) error { - - if err := deleteAssociatedReportsForDevice(d, w, lc, dbClient, errorHandler); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.DeleteError) - return err - } - - if err := dbClient.DeleteDeviceById(d.Id); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.DeleteError) - return err - } - - // Notify Associates - err := notifyDeviceAssociates(d, http.MethodDelete, ctx, lc, dbClient, nc, configuration) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Device.NotifyError) - return err - } - - return nil -} - -// Delete the associated device reports for the device -func deleteAssociatedReportsForDevice( - d models.Device, - w http.ResponseWriter, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) error { - - reports, err := dbClient.GetDeviceReportByDeviceName(d.Name) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusServiceUnavailable) - return err - } - - // Delete the associated reports - for _, report := range reports { - if err := dbClient.DeleteDeviceReportById(report.Id); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.DeleteError) - return err - } - _ = notifyDeviceReportAssociates(report, http.MethodDelete, lc, dbClient) - } - - return nil -} - -func restSetDeviceLastConnectedById( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) { - - vars := mux.Vars(r) - var did = vars[ID] - var vlc = vars[LASTCONNECTED] - lastConnected, err := strconv.ParseInt(vlc, 10, 64) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the device exists - d, err := dbClient.GetDeviceById(did) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.InternalServerError) - return - } - - ctx := r.Context() - // Update last connected - err = setLastConnected(d, lastConnected, false, w, ctx, lc, dbClient, errorHandler, nc, configuration) - if err != nil { - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restSetLastConnectedByIdNotify( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) { - - // Get the URL parameters - vars := mux.Vars(r) - var did = vars[ID] - var vlc = vars[LASTCONNECTED] - notify, err := strconv.ParseBool(vars[LASTCONNECTEDNOTIFY]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - lastConnected, err := strconv.ParseInt(vlc, 10, 64) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the device exists - d, err := dbClient.GetDeviceById(did) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.InternalServerError) - return - } - - ctx := r.Context() - // Update last connected - err = setLastConnected(d, lastConnected, notify, w, ctx, lc, dbClient, errorHandler, nc, configuration) - if err != nil { - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restSetDeviceLastConnectedByName( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusServiceUnavailable) - return - } - var vlc = vars[LASTCONNECTED] - lastConnected, err := strconv.ParseInt(vlc, 10, 64) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusServiceUnavailable) - return - } - - // Check if the device exists - d, err := dbClient.GetDeviceByName(n) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.ServiceUnavailable) - return - } - - ctx := r.Context() - // Update last connected - err = setLastConnected(d, lastConnected, false, w, ctx, lc, dbClient, errorHandler, nc, configuration) - if err != nil { - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restSetDeviceLastConnectedByNameNotify( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusServiceUnavailable) - return - } - var vlc = vars[LASTCONNECTED] - lastConnected, err := strconv.ParseInt(vlc, 10, 64) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusServiceUnavailable) - return - } - notify, err := strconv.ParseBool(vars[LASTCONNECTEDNOTIFY]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusServiceUnavailable) - return - } - - // Check if the device exists - d, err := dbClient.GetDeviceByName(n) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.ServiceUnavailable) - return - } - - ctx := r.Context() - // Update last connected - err = setLastConnected(d, lastConnected, notify, w, ctx, lc, dbClient, errorHandler, nc, configuration) - if err != nil { - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -// Update the last connected value for the device -func setLastConnected( - d models.Device, - time int64, - notify bool, - w http.ResponseWriter, - ctx context.Context, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) error { - - d.LastConnected = time - if err := dbClient.UpdateDevice(d); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.UpdateError_StatusServiceUnavailable) - return err - } - - if notify { - _ = notifyDeviceAssociates(d, http.MethodPut, ctx, lc, dbClient, nc, configuration) - } - - return nil -} - -func restSetDeviceLastReportedById( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) { - - vars := mux.Vars(r) - var did = vars[ID] - var vlr = vars[LASTREPORTED] - lr, err := strconv.ParseInt(vlr, 10, 64) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusServiceUnavailable) - return - } - - // Check if the device exists - d, err := dbClient.GetDeviceById(did) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.ServiceUnavailable) - return - } - - ctx := r.Context() - // Update Last Reported - if err = setLastReported( - d, - lr, - false, - w, - ctx, - lc, - dbClient, - errorHandler, - nc, - configuration); err != nil { - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restSetDeviceLastReportedByIdNotify( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) { - - vars := mux.Vars(r) - var did = vars[ID] - var vlr = vars[LASTREPORTED] - lr, err := strconv.ParseInt(vlr, 10, 64) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusServiceUnavailable) - return - } - notify, err := strconv.ParseBool(vars[LASTREPORTEDNOTIFY]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusServiceUnavailable) - return - } - - // Check if the device exists - d, err := dbClient.GetDeviceById(did) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.ServiceUnavailable) - return - } - - ctx := r.Context() - // Update last reported - err = setLastReported(d, lr, notify, w, ctx, lc, dbClient, errorHandler, nc, configuration) - if err != nil { - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restSetDeviceLastReportedByName( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - var vlr = vars[LASTREPORTED] - lr, err := strconv.ParseInt(vlr, 10, 64) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the device exists - d, err := dbClient.GetDeviceByName(n) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.InternalServerError) - return - } - - ctx := r.Context() - // Update last reported - if err = setLastReported( - d, - lr, - false, - w, - ctx, - lc, - dbClient, - errorHandler, - nc, - configuration); err != nil { - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restSetDeviceLastReportedByNameNotify( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - var vlr = vars[LASTREPORTED] - lr, err := strconv.ParseInt(vlr, 10, 64) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - notify, err := strconv.ParseBool(vars[LASTREPORTEDNOTIFY]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the device exists - d, err := dbClient.GetDeviceByName(n) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.InternalServerError) - return - } - - ctx := r.Context() - // Update last reported - err = setLastReported(d, lr, notify, w, ctx, lc, dbClient, errorHandler, nc, configuration) - if err != nil { - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -// Update the last reported field of the device -func setLastReported( - d models.Device, - time int64, - notify bool, - w http.ResponseWriter, - ctx context.Context, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) error { - - d.LastReported = time - if err := dbClient.UpdateDevice(d); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.UpdateError_StatusServiceUnavailable) - return err - } - - if notify { - _ = notifyDeviceAssociates(d, http.MethodPut, ctx, lc, dbClient, nc, configuration) - } - - return nil -} - -func restGetDeviceByName( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - dn, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - res, err := dbClient.GetDeviceByName(dn) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.InternalServerError) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -// Notify the associated device service for the device -func notifyDeviceAssociates( - d models.Device, - action string, - ctx context.Context, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) error { - - // Post the notification to the notifications service - postNotification(d.Name, action, ctx, nc, configuration) - - // Callback for device service - ds, err := dbClient.GetDeviceServiceById(d.Service.Id) - if err != nil { - lc.Error(err.Error()) - return err - } - if err := notifyAssociates([]models.DeviceService{ds}, d.Id, action, models.DEVICE, lc); err != nil { - lc.Error(err.Error()) - return err - } - - return nil -} - -func postNotification( - name string, - action string, - ctx context.Context, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) { - - // Only post notification if the configuration is set - if configuration.Notifications.PostDeviceChanges { - // Make the notification - notification := notifications.Notification{ - Slug: configuration.Notifications.Slug + strconv.FormatInt(db.MakeTimestamp(), 10), - Content: configuration.Notifications.Content + name + "-" + action, - Category: notifications.SW_HEALTH, - Description: configuration.Notifications.Description, - Labels: []string{configuration.Notifications.Label}, - Sender: configuration.Notifications.Sender, - Severity: notifications.NORMAL, - } - - _ = nc.SendNotification(ctx, notification) - } -} diff --git a/internal/core/metadata/rest_device_test.go b/internal/core/metadata/rest_device_test.go deleted file mode 100644 index 7101e9fcdf..0000000000 --- a/internal/core/metadata/rest_device_test.go +++ /dev/null @@ -1,84 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 metadata - -import ( - "net/http" - "net/http/httptest" - "testing" - - metadataConfig "github.com/edgexfoundry/edgex-go/internal/core/metadata/config" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/pkg/errors" -) - -func TestGetAllDevices(t *testing.T) { - tests := []struct { - name string - dbMock interfaces.DBClient - expectedStatus int - }{ - {"OK", createGetDeviceLoaderMock(1), http.StatusOK}, - {"MaxExceeded", createGetDeviceLoaderMock(2), http.StatusRequestEntityTooLarge}, - {"Unexpected", createGetDeviceLoaderMockFail(), http.StatusInternalServerError}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - configuration := metadataConfig.ConfigurationStruct{ - Service: bootstrapConfig.ServiceInfo{MaxResultCount: 1}, - } - restGetAllDevices( - rr, - loggerMock, - tt.dbMock, - errorconcept.NewErrorHandler(loggerMock), - &configuration) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func createGetDeviceLoaderMock(howMany int) interfaces.DBClient { - - devices := []contract.Device{} - for i := 0; i < howMany; i++ { - devices = append(devices, contract.Device{Name: "Some Device"}) - } - - dbMock := &mocks.DBClient{} - dbMock.On("GetAllDevices").Return(devices, nil) - return dbMock -} - -func createGetDeviceLoaderMockFail() interfaces.DBClient { - - dbMock := &mocks.DBClient{} - dbMock.On("GetAllDevices").Return(nil, errors.New("unexpected error")) - return dbMock -} diff --git a/internal/core/metadata/rest_deviceprofile.go b/internal/core/metadata/rest_deviceprofile.go deleted file mode 100644 index 4f514e79fe..0000000000 --- a/internal/core/metadata/rest_deviceprofile.go +++ /dev/null @@ -1,613 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * - * 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 metadata - -import ( - "encoding/json" - "io/ioutil" - "net/http" - "net/url" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/config" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/device" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/device_profile" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/coredata" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" - "gopkg.in/yaml.v2" -) - -func restGetAllDeviceProfiles( - w http.ResponseWriter, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - op := device_profile.NewGetAllExecutor(configuration.Service, dbClient, lc) - res, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.LimitExceeded, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(&res) -} - -func restAddDeviceProfile( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - vdc coredata.ValueDescriptorClient, - configuration *config.ConfigurationStruct) { - - data, err := ioutil.ReadAll(r.Body) - if err != nil { - errorHandler.Handle(w, err, errorconcept.DeviceProfile.ReadFile) - return - } - - addDeviceProfile(data, lc, vdc, dbClient, r, w, errorHandler, configuration) -} - -func restUpdateDeviceProfile( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - vdc coredata.ValueDescriptorClient, - configuration *config.ConfigurationStruct) { - - defer r.Body.Close() - - var from models.DeviceProfile - if err := json.NewDecoder(r.Body).Decode(&from); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - if configuration.Writable.EnableValueDescriptorManagement { - vdOp := device_profile.NewUpdateValueDescriptorExecutor(r.Context(), from, dbClient, vdc, lc) - err := vdOp.Execute() - if err != nil { - errorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.NewServiceClientHttpError(err), - errorconcept.DeviceProfile.NotFound, - errorconcept.ValueDescriptors.MultipleInUse, - errorconcept.DeviceProfile.InvalidState_StatusConflict, - }, - errorconcept.Default.InternalServerError) - return - } - } - - op := device_profile.NewUpdateDeviceProfileExecutor(dbClient, from) - dp, err := op.Execute() - if err != nil { - errorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.DeviceProfile.NotFound, - errorconcept.DeviceProfile.InvalidState_StatusConflict, - }, - errorconcept.Default.InternalServerError) - return - } - - // Notify Associates - err = notifyProfileAssociates(dp, dbClient, http.MethodPut, lc, errorHandler, configuration) - if err != nil { - // Log the error but do not change the response to the client. We do not want this to affect the overall status - // of the operation - lc.Warn("Error while notifying profile associates of update: ", err.Error()) - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restGetProfileByProfileId( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var did = vars["id"] - - op := device_profile.NewGetProfileID(did, dbClient) - res, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.InternalServerError) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -func restDeleteProfileByProfileId( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var did = vars["id"] - - op := device_profile.NewDeleteByIDExecutor(dbClient, did) - err := op.Execute() - if err != nil { - errorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.DeviceProfile.NotFound, - errorconcept.DeviceProfile.InvalidState_StatusConflict, - }, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -// Delete the device profile based on its name -func restDeleteProfileByName( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - op := device_profile.NewDeleteByNameExecutor(dbClient, n) - err = op.Execute() - if err != nil { - errorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.DeviceProfile.NotFound, - errorconcept.DeviceProfile.InvalidState_StatusConflict, - }, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restAddProfileByYaml( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - vdc coredata.ValueDescriptorClient, - configuration *config.ConfigurationStruct) { - - f, _, err := r.FormFile("file") - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.DeviceProfile.MissingFile, - errorconcept.Default.InternalServerError) - return - } - - data, err := ioutil.ReadAll(f) - if err != nil { - errorHandler.Handle(w, err, errorconcept.DeviceProfile.ReadFile) - return - } - if len(data) == 0 { - err := errors.NewErrEmptyFile("YAML") - errorHandler.Handle(w, err, errorconcept.DeviceProfile.MissingFile) - return - } - - var dp models.DeviceProfile - - err = yaml.Unmarshal(data, &dp) - if err != nil { - errorHandler.Handle(w, err, errorconcept.DeviceProfile.UnmarshalYaml_StatusInternalServer) - return - } - - jsonBytes, err := json.Marshal(dp) - if err != nil { - errorHandler.Handle(w, err, errorconcept.DeviceProfile.MarshalJson) - return - } - - addDeviceProfile(jsonBytes, lc, vdc, dbClient, r, w, errorHandler, configuration) -} - -// Add a device profile with YAML content -// The YAML content is passed as a string in the http request -func restAddProfileByYamlRaw( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - vdc coredata.ValueDescriptorClient, - configuration *config.ConfigurationStruct) { - - // Get the YAML string - body, err := ioutil.ReadAll(r.Body) - if err != nil { - errorHandler.Handle(w, err, errorconcept.DeviceProfile.ReadFile) - return - } - - var dp models.DeviceProfile - - err = yaml.Unmarshal(body, &dp) - if err != nil { - errorHandler.Handle(w, err, errorconcept.DeviceProfile.UnmarshalYaml_StatusServiceUnavailable) - return - } - - jsonBytes, err := json.Marshal(dp) - if err != nil { - errorHandler.Handle(w, err, errorconcept.DeviceProfile.MarshalJson) - return - } - - addDeviceProfile(jsonBytes, lc, vdc, dbClient, r, w, errorHandler, configuration) -} - -// This function centralizes the common logic for adding a device profile to the database and dealing with the return -func addDeviceProfile( - jsonBytes []byte, - lc logger.LoggingClient, - vdc coredata.ValueDescriptorClient, - dbClient interfaces.DBClient, - r *http.Request, - w http.ResponseWriter, - errorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - var dp models.DeviceProfile - if err := dp.UnmarshalJSON(jsonBytes); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - if configuration.Writable.EnableValueDescriptorManagement { - // Check if the device profile name is unique so that we do not create ValueDescriptors for a DeviceProfile that - // will fail during the creation process later on. - nameOp := device_profile.NewGetProfileName(dp.Name, dbClient) - _, err := nameOp.Execute() - // The operator will return an DuplicateName error if the DeviceProfile exist. - if err == nil { - errorHandler.Handle(w, err, errorconcept.DeviceProfile.DuplicateName) - return - } - - op := device_profile.NewAddValueDescriptorExecutor(r.Context(), vdc, lc, dp.DeviceResources...) - err = op.Execute() - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.NewServiceClientHttpError(err), - errorconcept.Default.InternalServerError) - return - } - } - - op := device_profile.NewAddDeviceProfileExecutor(dp, dbClient) - id, err := op.Execute() - - if err != nil { - errorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.Common.ContractInvalid_StatusBadRequest, - errorconcept.DeviceProfile.InvalidState_StatusBadRequest, - errorconcept.Common.DuplicateName, - errorconcept.DeviceProfile.EmptyName, - }, - errorconcept.Default.InternalServerError) - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte(id)) -} - -func restGetProfileByModel( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - an, err := url.QueryUnescape(vars[MODEL]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - op := device_profile.NewGetModelExecutor(an, dbClient) - res, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.LimitExceeded, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -func restGetProfileWithLabel( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - - label, err := url.QueryUnescape(vars[LABEL]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - op := device_profile.NewGetLabelExecutor(label, dbClient) - res, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.LimitExceeded, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -func restGetProfileByManufacturerModel( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - man, err := url.QueryUnescape(vars[MANUFACTURER]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - mod, err := url.QueryUnescape(vars[MODEL]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - op := device_profile.NewGetManufacturerModelExecutor(man, mod, dbClient) - res, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.LimitExceeded, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -func restGetProfileByManufacturer( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - man, err := url.QueryUnescape(vars[MANUFACTURER]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - op := device_profile.NewGetManufacturerExecutor(man, dbClient) - res, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.LimitExceeded, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -func restGetProfileByName( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - dn, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Get the device - op := device_profile.NewGetProfileName(dn, dbClient) - res, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -func restGetYamlProfileByName( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - name, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check for the device profile - op := device_profile.NewGetProfileName(name, dbClient) - dp, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.InternalServerError) - return - } - - // Marshal into yaml - out, err := yaml.Marshal(dp) - if err != nil { - errorHandler.Handle(w, err, errorconcept.DeviceProfile.MarshalYaml) - return - } - - w.WriteHeader(http.StatusOK) - w.Write(out) -} - -// restGetYamlProfileById looks up a device profile by its ID. It will be output in a YAML formatted string. -func restGetYamlProfileById( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - id := vars[ID] - - // Check if the device profile exists - op := device_profile.NewGetProfileID(id, dbClient) - dp, err := op.Execute() - if err != nil { - if err == db.ErrNotFound { - errorHandler.Handle(w, err, errorconcept.Default.NotFound) - w.Write([]byte(nil)) - return - } else { - errorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - } - lc.Error(err.Error()) - return - } - - // Marshal the device profile into YAML - out, err := yaml.Marshal(dp) - if err != nil { - errorHandler.Handle(w, err, errorconcept.DeviceProfile.MarshalYaml) - return - } - - w.WriteHeader(http.StatusOK) - w.Write(out) -} - -// Notify the associated device services for changes in the device profile -func notifyProfileAssociates( - dp models.DeviceProfile, - dl device.DeviceLoader, - action string, - lc logger.LoggingClient, - errorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) error { - - // Get the devices - op := device.NewProfileIdExecutor(configuration.Service, dl, lc, dp.Id) - d, err := op.Execute() - if err != nil { - lc.Error(err.Error()) - return err - } - - // Get the services for each dev - // Use map as a Set - dsMap := map[string]models.DeviceService{} - ds := []models.DeviceService{} - for _, dev := range d { - // Only add if not there - if _, ok := dsMap[dev.Service.Id]; !ok { - dsMap[dev.Service.Id] = dev.Service - ds = append(ds, dev.Service) - } - } - - if err := notifyAssociates(ds, dp.Id, action, models.PROFILE, lc); err != nil { - lc.Error(err.Error()) - return err - } - - return nil -} diff --git a/internal/core/metadata/rest_deviceprofile_test.go b/internal/core/metadata/rest_deviceprofile_test.go deleted file mode 100644 index d0a02e95af..0000000000 --- a/internal/core/metadata/rest_deviceprofile_test.go +++ /dev/null @@ -1,1610 +0,0 @@ -package metadata - -import ( - "bytes" - "context" - "encoding/json" - goErrors "errors" - "mime/multipart" - "net/http" - "net/http/httptest" - "testing" - - metadataConfig "github.com/edgexfoundry/edgex-go/internal/core/metadata/config" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/types" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" - "github.com/stretchr/testify/mock" - "gopkg.in/yaml.v2" -) - -var TestLabelError1 = "TestErrorLabel1" -var TestLabelError2 = "TestErrorLabel2" -var TestDeviceProfileLabel1 = "TestLabel1" -var TestDeviceProfileLabel2 = "TestLabel2" -var TestDeviceProfileLabels = []string{TestDeviceProfileLabel1, TestDeviceProfileLabel2} -var TestDeviceProfileManufacturer = "TestManufacturer" -var TestDeviceProfileModel = "TestModel" -var TestDeviceProfiles = []contract.DeviceProfile{ - TestDeviceProfile, - createTestDeviceProfileWithCommands( - "TestDeviceProfileID2", - "TestDeviceProfileName2", - []string{TestDeviceProfileLabel1, - TestDeviceProfileLabel2}, - TestDeviceProfileManufacturer, - TestDeviceProfileModel, - TestCommand), - createTestDeviceProfileWithCommands( - "TestErrorID", - "TestErrorName", - []string{TestLabelError1, - TestLabelError2}, - "TestErrorManufacturer", - "TestErrorModel", - TestCommand), -} -var TestDeviceProfileValidated = createValidatedTestDeviceProfile() -var TestError = goErrors.New("test error") -var TestDeviceProfileID = "TestProfileID" -var TestDeviceProfileName = "TestProfileName" -var TestDeviceProfile = createTestDeviceProfile() -var TestCommand = contract.Command{Name: "TestCommand", Id: "TestCommandId"} -var TestDevices = []contract.Device{ - { - Name: "TestDevice1", - }, - { - Name: "TestDevice2", - }, -} - -var TestProvisionWatchers = []contract.ProvisionWatcher{ - { - Name: "TestProvisionWatcher1", - }, - { - Name: "TestProvisionWatcher2", - }, -} - -var TestDeleteDeviceResource = contract.DeviceResource{Name: "TestDeleteDeviceResource"} -var TestUpdateDeviceResource = contract.DeviceResource{Name: "TestUpdateDeviceResource"} -var TestInUseDeviceResource = contract.DeviceResource{Name: "TestInUseDeviceResource"} - -func TestGetAllProfiles(t *testing.T) { - tests := []struct { - name string - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createDBClient(), - http.StatusOK, - }, - { - "Max result count exceeded", - createDBClientGetDeviceProfileMaxLimitError(), - http.StatusRequestEntityTooLarge, - }, - { - "Device Profile Not Found", - createDBClientDeviceProfileErrorNotFound(), - http.StatusInternalServerError, - }, - { - "Database error", - createDBClientGetDeviceProfileError(), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - configuration := metadataConfig.ConfigurationStruct{ - Service: bootstrapConfig.ServiceInfo{MaxResultCount: len(TestDeviceProfiles)}, - } - restGetAllDeviceProfiles( - rr, - loggerMock, - tt.dbMock, - errorconcept.NewErrorHandler(loggerMock), - &configuration) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestAddDeviceProfile(t *testing.T) { - // this one uses validated because it needs it for input and the dbMock - emptyName := TestDeviceProfileValidated - emptyName.Name = "" - requestContext := httptest.NewRequest(http.MethodPost, AddressableTestURI, nil).Context() - - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - vdcMock MockValueDescriptorClient - enableValueDescriptorManagement bool - expectedStatus int - }{ - { - "OK", - createRequestWithBody(http.MethodPost, TestDeviceProfile), - createDBClientWithOutlines([]mockOutline{ - {"AddDeviceProfile", []interface{}{TestDeviceProfileValidated}, []interface{}{TestDeviceProfileID, nil}}, - {"GetDeviceProfileByName", []interface{}{TestDeviceProfileName}, []interface{}{contract.DeviceProfile{}, db.ErrNotFound}}, - }), - createMockValueDescriptorClient([]mockOutline{ - {"Add", []interface{}{TestDeviceProfileValidated}, []interface{}{}}, - }, requestContext, TestDeviceProfile, nil), - true, - http.StatusOK, - }, - { - "OK with value descriptor management", - createRequestWithBody(http.MethodPost, TestDeviceProfile), - createDBClientWithOutlines([]mockOutline{ - {"AddDeviceProfile", []interface{}{TestDeviceProfileValidated}, []interface{}{TestDeviceProfileID, nil}}, - {"GetDeviceProfileByName", []interface{}{TestDeviceProfileName}, []interface{}{contract.DeviceProfile{}, db.ErrNotFound}}, - }), - createMockValueDescriptorClient([]mockOutline{ - {"Add", []interface{}{TestDeviceProfileValidated}, []interface{}{}}, - }, requestContext, TestDeviceProfile, nil), - true, - http.StatusOK, - }, - { - "Value descriptor management service client error", - createRequestWithBody(http.MethodPost, TestDeviceProfile), - createDBClientWithOutlines([]mockOutline{ - {"GetDeviceProfileByName", []interface{}{TestDeviceProfileName}, []interface{}{contract.DeviceProfile{}, db.ErrNotFound}}, - }), - createMockValueDescriptorClient([]mockOutline{ - {"Add", []interface{}{TestDeviceProfileValidated}, []interface{}{}}, - }, requestContext, TestDeviceProfile, types.ErrServiceClient{StatusCode: http.StatusTeapot}), - true, - http.StatusTeapot, - }, - { - "Value descriptor management service other error", - createRequestWithBody(http.MethodPost, TestDeviceProfile), - createDBClientWithOutlines([]mockOutline{ - {"GetDeviceProfileByName", []interface{}{TestDeviceProfileName}, []interface{}{contract.DeviceProfile{}, db.ErrNotFound}}, - }), - createMockValueDescriptorClient([]mockOutline{ - {"Add", []interface{}{TestDeviceProfileValidated}, []interface{}{}}, - }, requestContext, TestDeviceProfile, TestError), - true, - http.StatusInternalServerError, - }, - { - "YAML unmarshal error", - httptest.NewRequest(http.MethodPost, AddressableTestURI, bytes.NewBuffer(nil)), - nil, - createMockValueDescriptorClient([]mockOutline{}, requestContext, TestDeviceProfile, nil), - true, - http.StatusBadRequest, - }, - { - "Duplicate commands", - createRequestWithBody(http.MethodPost, createTestDeviceProfileWithCommands(TestDeviceProfileID, TestDeviceProfileName, TestDeviceProfileLabels, TestDeviceProfileManufacturer, TestDeviceProfileModel, TestCommand, TestCommand)), - nil, - createMockValueDescriptorClient([]mockOutline{}, requestContext, TestDeviceProfile, nil), - true, - http.StatusBadRequest, - }, - { - "Empty device profile name", - createRequestWithBody(http.MethodPost, emptyName), - createDBClientWithOutlines([]mockOutline{ - {"AddDeviceProfile", []interface{}{emptyName}, []interface{}{"", db.ErrNameEmpty}}, - {"GetDeviceProfileByName", []interface{}{""}, []interface{}{contract.DeviceProfile{}, db.ErrNotFound}}, - }), - createMockValueDescriptorClient([]mockOutline{ - {"Add", []interface{}{TestDeviceProfileValidated}, []interface{}{}}, - }, requestContext, TestDeviceProfile, nil), - true, - http.StatusBadRequest, - }, - { - "Unsuccessful database call", - createRequestWithBody(http.MethodPost, TestDeviceProfile), - createDBClientWithOutlines([]mockOutline{ - {"AddDeviceProfile", []interface{}{TestDeviceProfileValidated}, []interface{}{"", TestError}}, - {"GetDeviceProfileByName", []interface{}{TestDeviceProfileName}, []interface{}{contract.DeviceProfile{}, db.ErrNotFound}}, - }), - createMockValueDescriptorClient([]mockOutline{ - {"Add", []interface{}{TestDeviceProfileValidated}, []interface{}{}}, - }, requestContext, TestDeviceProfile, nil), - true, - http.StatusInternalServerError, - }, - { - "Add device profile without enable value descriptor management", - createRequestWithBody(http.MethodPost, TestDeviceProfile), - createDBClientWithOutlines([]mockOutline{ - {"AddDeviceProfile", []interface{}{TestDeviceProfileValidated}, []interface{}{TestDeviceProfileID, nil}}, - {"GetDeviceProfileByName", []interface{}{TestDeviceProfileName}, []interface{}{contract.DeviceProfile{}, db.ErrNotFound}}, - }), - createMockValueDescriptorClient([]mockOutline{}, requestContext, TestDeviceProfile, nil), - false, - http.StatusOK, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - configuration := metadataConfig.ConfigurationStruct{ - Writable: metadataConfig.WritableInfo{EnableValueDescriptorManagement: tt.enableValueDescriptorManagement}, - Service: bootstrapConfig.ServiceInfo{MaxResultCount: 1}, - } - restAddDeviceProfile( - rr, - tt.request, - loggerMock, - tt.dbMock, - errorconcept.NewErrorHandler(loggerMock), - tt.vdcMock, - &configuration) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - tt.vdcMock.AssertExpectations(t) - }) - } -} - -func TestGetProfileById(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createRequestWithPathParameters(http.MethodGet, map[string]string{ID: TestDeviceProfileID}), - createDBClient(), - http.StatusOK, - }, - { - "Device Profile Not Found", - createRequestWithPathParameters(http.MethodGet, map[string]string{ID: TestDeviceProfileID}), - createDBClientDeviceProfileErrorNotFound(), - http.StatusNotFound, - }, - { - "Database error", - createRequestWithPathParameters(http.MethodGet, map[string]string{ID: TestDeviceProfileID}), - createDBClientGetDeviceProfileError(), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetProfileByProfileId(rr, tt.request, tt.dbMock, errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestGetYamlProfileById(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createRequestWithPathParameters(http.MethodGet, map[string]string{ID: TestDeviceProfileID}), - createDBClient(), - http.StatusOK, - }, - { - "Device Profile Not Found", - createRequestWithPathParameters(http.MethodGet, map[string]string{ID: TestDeviceProfileID}), - createDBClientDeviceProfileErrorNotFound(), - http.StatusNotFound, - }, - { - "Database error", - createRequestWithPathParameters(http.MethodGet, map[string]string{ID: TestDeviceProfileID}), - createDBClientGetDeviceProfileError(), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - restGetYamlProfileById( - rr, - tt.request, - loggerMock, - tt.dbMock, - errorconcept.NewErrorHandler(loggerMock)) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestGetProfileByName(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createRequestWithPathParameters(http.MethodGet, map[string]string{NAME: TestDeviceProfileName}), - createDBClient(), - http.StatusOK, - }, - { - "Invalid name", - createRequestWithPathParameters(http.MethodGet, map[string]string{NAME: ErrorPathParam}), - createDBClient(), - http.StatusBadRequest, - }, - { - "Device Profile Not Found", - createRequestWithPathParameters(http.MethodGet, map[string]string{NAME: TestDeviceProfileName}), - createDBClientDeviceProfileErrorNotFound(), - http.StatusNotFound, - }, - { - "Database error", - createRequestWithPathParameters(http.MethodGet, map[string]string{NAME: TestDeviceProfileName}), - createDBClientGetDeviceProfileError(), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetProfileByName(rr, tt.request, tt.dbMock, errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestGetYamlProfileByName(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createRequestWithPathParameters(http.MethodGet, map[string]string{NAME: TestDeviceProfileName}), - createDBClient(), - http.StatusOK, - }, - { - "Invalid name", - createRequestWithPathParameters(http.MethodGet, map[string]string{NAME: ErrorPathParam}), - createDBClient(), - http.StatusBadRequest, - }, - { - "Device Profile Not Found", - createRequestWithPathParameters(http.MethodGet, map[string]string{NAME: TestDeviceProfileName}), - createDBClientDeviceProfileErrorNotFound(), - http.StatusNotFound, - }, - { - "Database error", - createRequestWithPathParameters(http.MethodGet, map[string]string{NAME: TestDeviceProfileName}), - createDBClientGetDeviceProfileError(), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetYamlProfileByName(rr, tt.request, tt.dbMock, errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestGetProfileByLabel(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createRequestWithPathParameters(http.MethodGet, map[string]string{LABEL: TestDeviceProfileLabel1}), - createDBClient(), - http.StatusOK, - }, - { - "Invalid name", - createRequestWithPathParameters(http.MethodGet, map[string]string{LABEL: ErrorPathParam}), - createDBClient(), - http.StatusBadRequest, - }, - { - "Device Profile Not Found", - createRequestWithPathParameters(http.MethodGet, map[string]string{LABEL: TestDeviceProfileLabel1}), - createDBClientDeviceProfileErrorNotFound(), - http.StatusInternalServerError, - }, - { - "Database error", - createRequestWithPathParameters(http.MethodGet, map[string]string{LABEL: TestDeviceProfileLabel1}), - createDBClientGetDeviceProfileError(), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetProfileWithLabel(rr, tt.request, tt.dbMock, errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestGetProfileByManufacturer(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createRequestWithPathParameters(http.MethodGet, map[string]string{MANUFACTURER: TestDeviceProfileManufacturer}), - createDBClient(), - http.StatusOK, - }, - { - "Invalid name", - createRequestWithPathParameters(http.MethodGet, map[string]string{MANUFACTURER: ErrorPathParam}), - createDBClient(), - http.StatusBadRequest, - }, - { - "Device Profile Not Found", - createRequestWithPathParameters(http.MethodGet, map[string]string{MANUFACTURER: TestDeviceProfileManufacturer}), - createDBClientDeviceProfileErrorNotFound(), - http.StatusInternalServerError, - }, - { - "Database error", - createRequestWithPathParameters(http.MethodGet, map[string]string{MANUFACTURER: TestDeviceProfileManufacturer}), - createDBClientGetDeviceProfileError(), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetProfileByManufacturer( - rr, - tt.request, - tt.dbMock, - errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestGetProfileByModel(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createRequestWithPathParameters(http.MethodGet, map[string]string{MODEL: TestDeviceProfileModel}), - createDBClient(), - http.StatusOK, - }, - { - "Invalid MODEL", - createRequestWithPathParameters(http.MethodGet, map[string]string{MODEL: ErrorPathParam}), - createDBClient(), - http.StatusBadRequest, - }, - { - "Device Profile Not Found", - createRequestWithPathParameters(http.MethodGet, map[string]string{MODEL: TestDeviceProfileModel}), - createDBClientDeviceProfileErrorNotFound(), - http.StatusInternalServerError, - }, - { - "Database error", - createRequestWithPathParameters(http.MethodGet, map[string]string{MODEL: TestDeviceProfileModel}), - createDBClientGetDeviceProfileError(), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetProfileByModel(rr, tt.request, tt.dbMock, errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestGetProfileByManufacturerModel(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createRequestWithPathParameters(http.MethodGet, - map[string]string{ - MANUFACTURER: TestDeviceProfileManufacturer, - MODEL: TestDeviceProfileModel, - }), - createDBClient(), - http.StatusOK, - }, - { - "Invalid MANUFACTURER", - createRequestWithPathParameters(http.MethodGet, - map[string]string{ - MANUFACTURER: ErrorPathParam, - MODEL: TestDeviceProfileModel, - }), - createDBClient(), - http.StatusBadRequest, - }, - { - "Invalid MODEL", - createRequestWithPathParameters(http.MethodGet, - map[string]string{ - MANUFACTURER: TestDeviceProfileManufacturer, - MODEL: ErrorPathParam, - }), - createDBClient(), - http.StatusBadRequest, - }, - { - "Device Profile Not Found", - createRequestWithPathParameters(http.MethodGet, - map[string]string{ - MANUFACTURER: TestDeviceProfileManufacturer, - MODEL: TestDeviceProfileModel, - }), createDBClientDeviceProfileErrorNotFound(), - http.StatusInternalServerError, - }, - { - "Database error", - createRequestWithPathParameters(http.MethodGet, - map[string]string{ - MANUFACTURER: TestDeviceProfileManufacturer, - MODEL: TestDeviceProfileModel, - }), - createDBClientGetDeviceProfileError(), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetProfileByManufacturerModel( - rr, - tt.request, - tt.dbMock, - errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestUpdateDeviceProfile(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - enableValueDescriptorManagement bool - expectedStatus int - }{ - { - "OK", - createRequestWithBody(http.MethodPut, TestDeviceProfile), - createDBClient(), - true, - http.StatusOK, - }, - { - "ValueDescriptor in use error", - createRequestWithBody(http.MethodPut, TestDeviceProfile), - createDBClientPersistDeviceInUseError(), - true, - http.StatusConflict, - }, - { - "ValueDescriptor update error", - createRequestWithBody(http.MethodPut, TestDeviceProfile), - createDBClientUpdateValueDescriptorError(), - true, - http.StatusInternalServerError, - }, - { - "Multiple devices associated with device profile", - createRequestWithBody(http.MethodPut, TestDeviceProfile), - createDBClientMultipleDevicesFoundError(), - true, - http.StatusConflict, - }, - { - "Multiple provision watchers associated with device profile", - createRequestWithBody(http.MethodPut, TestDeviceProfile), - createDBClientMultipleProvisionWatchersFoundError(), - true, - http.StatusConflict, - }, - { - "Invalid request body", - createRequestWithInvalidBody(), - createDBClient(), - true, - http.StatusBadRequest, - }, - { - "Device Profile Not Found(ValueDescriptorExecutor)", - createRequestWithBody(http.MethodPut, TestDeviceProfile), - createMockErrDeviceProfileNotFound(), - true, - http.StatusNotFound, - }, - { - "Device Profile Not Found(UpdateDeviceProfileExecutor)", - createRequestWithBody(http.MethodPut, TestDeviceProfile), - createMockErrDeviceProfileNotFound(), - false, - http.StatusNotFound, - }, - { - "GetProvisionWatchersByProfileId database error ", - createRequestWithBody(http.MethodPut, TestDeviceProfile), - createDBClientGetProvisionWatchersByProfileIdError(), - true, - http.StatusInternalServerError, - }, - { - "Service client error ", - createRequestWithBody(http.MethodPut, TestDeviceProfile), - createDBServiceClientError(http.StatusTeapot), - true, - http.StatusTeapot, - }, - { - "UpdateDeviceProfile database error ", - createRequestWithBody(http.MethodPut, TestDeviceProfile), - createDBClientPersistDeviceProfileError(), - true, - http.StatusInternalServerError, - }, - { - "GetDevicesByProfileId database error", - createRequestWithBody(http.MethodPut, TestDeviceProfile), - createDBClientGetDevicesByProfileIdError(), - true, - http.StatusInternalServerError, - }, - { - "Duplicate commands error ", - createRequestWithBody(http.MethodPut, createTestDeviceProfileWithCommands(TestDeviceProfileID, TestDeviceProfileName, TestDeviceProfileLabels, TestDeviceProfileManufacturer, TestDeviceProfileModel, TestCommand, TestCommand)), - createDBClient(), - true, - http.StatusBadRequest, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - configuration := metadataConfig.ConfigurationStruct{ - Writable: metadataConfig.WritableInfo{ - EnableValueDescriptorManagement: tt.enableValueDescriptorManagement}, - Service: bootstrapConfig.ServiceInfo{MaxResultCount: 1}, - } - restUpdateDeviceProfile( - rr, - tt.request, - loggerMock, - tt.dbMock, - errorconcept.NewErrorHandler(loggerMock), - MockValueDescriptorClient{}, - &configuration) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestDeleteDeviceProfileById(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createRequestWithPathParameters(http.MethodDelete, map[string]string{ID: TestDeviceProfileID}), - createDBClient(), - http.StatusOK, - }, - { - "Multiple devices associated with device profile", - createRequestWithPathParameters(http.MethodDelete, map[string]string{ID: TestDeviceProfileID}), - createDBClientMultipleDevicesFoundError(), - http.StatusConflict, - }, - { - "Multiple provision watchers associated with device profile", - createRequestWithPathParameters(http.MethodDelete, map[string]string{ID: TestDeviceProfileID}), - createDBClientMultipleProvisionWatchersFoundError(), - http.StatusConflict, - }, - { - "Device Profile Not Found", - createRequestWithPathParameters(http.MethodDelete, map[string]string{ID: TestDeviceProfileID}), - createDBClientDeviceProfileErrorNotFound(), - http.StatusNotFound, - }, - { - "GetProvisionWatchersByProfileId database error ", - createRequestWithPathParameters(http.MethodDelete, map[string]string{ID: TestDeviceProfileID}), - createDBClientGetProvisionWatchersByProfileIdError(), - http.StatusInternalServerError, - }, - { - "DeleteDeviceProfileById database error ", - createRequestWithPathParameters(http.MethodDelete, map[string]string{ID: TestDeviceProfileID}), - createDBClientPersistDeviceProfileError(), - http.StatusInternalServerError, - }, - { - "GetDevicesByProfileId database error", - createRequestWithPathParameters(http.MethodDelete, map[string]string{ID: TestDeviceProfileID}), - createDBClientGetDevicesByProfileIdError(), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restDeleteProfileByProfileId( - rr, - tt.request, - tt.dbMock, - errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestDeleteDeviceProfileByName(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createRequestWithPathParameters(http.MethodGet, map[string]string{NAME: TestDeviceProfileName}), - createDBClient(), - http.StatusOK, - }, - { - "Invalid name", - createRequestWithPathParameters(http.MethodGet, map[string]string{NAME: ErrorPathParam}), - createDBClient(), - http.StatusBadRequest, - }, - { - "Multiple devices associated with device profile", - createRequestWithPathParameters(http.MethodGet, map[string]string{NAME: TestDeviceProfileName}), - createDBClientMultipleDevicesFoundError(), - http.StatusConflict, - }, - { - "Multiple provision watchers associated with device profile", - createRequestWithPathParameters(http.MethodGet, map[string]string{NAME: TestDeviceProfileName}), - createDBClientMultipleProvisionWatchersFoundError(), - http.StatusConflict, - }, - { - "Device Profile Not Found", - createRequestWithPathParameters(http.MethodGet, map[string]string{NAME: TestDeviceProfileName}), - createDBClientDeviceProfileErrorNotFound(), - http.StatusNotFound, - }, - { - "GetProvisionWatchersByProfileId database error ", - createRequestWithPathParameters(http.MethodGet, map[string]string{NAME: TestDeviceProfileName}), - createDBClientGetProvisionWatchersByProfileIdError(), - http.StatusInternalServerError, - }, - { - "DeleteDeviceProfileById database error ", - createRequestWithPathParameters(http.MethodGet, map[string]string{NAME: TestDeviceProfileName}), - createDBClientPersistDeviceProfileError(), - http.StatusInternalServerError, - }, - { - "GetDevicesByProfileId database error", - createRequestWithPathParameters(http.MethodGet, map[string]string{NAME: TestDeviceProfileName}), - createDBClientGetDevicesByProfileIdError(), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restDeleteProfileByName(rr, tt.request, tt.dbMock, errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestAddProfileByYaml(t *testing.T) { - // we have to overwrite the CoreCommands so that their isValidated is false - dp := TestDeviceProfileValidated - - okBody, _ := yaml.Marshal(dp) - - duplicateCommandName := dp - duplicateCommandName.CoreCommands = []contract.Command{TestCommand, TestCommand} - dupeBody, _ := yaml.Marshal(duplicateCommandName) - - emptyName := dp - emptyName.Name = "" - emptyBody, _ := yaml.Marshal(emptyName) - - emptyDeviceResource := createTestDeviceProfile() - emptyDeviceResource.DeviceCommands[0].Get = []contract.ResourceOperation{ - {Index: "test index", Operation: "test operation", DeviceResource: ""}, - } - emptyDeviceResourceBody, _ := yaml.Marshal(emptyDeviceResource) - - emptyCoreCmdName := createTestDeviceProfile() - emptyCoreCmdName.CoreCommands[0].Name = "" - emptyCoreCmdNameBody, _ := yaml.Marshal(emptyCoreCmdName) - - emptyFileRequest := createDeviceProfileRequestWithFile(okBody) - emptyFileRequest.MultipartForm = new(multipart.Form) - emptyFileRequest.MultipartForm.File = nil - requestContext := httptest.NewRequest(http.MethodPost, AddressableTestURI, nil).Context() - - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - vdcMock MockValueDescriptorClient - enableValueDescriptorManagement bool - expectedStatus int - }{ - { - "OK", - createDeviceProfileRequestWithFile(okBody), - createDBClientWithOutlines([]mockOutline{ - {"AddDeviceProfile", []interface{}{dp}, []interface{}{TestDeviceProfileID, nil}}, - {"GetDeviceProfileByName", []interface{}{TestDeviceProfileName}, []interface{}{contract.DeviceProfile{}, db.ErrNotFound}}, - }), - createMockValueDescriptorClient([]mockOutline{ - {"Add", []interface{}{TestDeviceProfileValidated}, []interface{}{}}, - }, requestContext, TestDeviceProfile, nil), - true, - http.StatusOK, - }, - { - "Wrong content type", - httptest.NewRequest(http.MethodPut, AddressableTestURI, bytes.NewBuffer(okBody)), - nil, - createMockValueDescriptorClient([]mockOutline{}, requestContext, TestDeviceProfile, nil), - true, - http.StatusInternalServerError, - }, - { - "Missing file", - emptyFileRequest, - nil, - createMockValueDescriptorClient([]mockOutline{}, requestContext, TestDeviceProfile, nil), - true, - http.StatusBadRequest, - }, - { - "Empty file", - createDeviceProfileRequestWithFile([]byte{}), - nil, - createMockValueDescriptorClient([]mockOutline{}, requestContext, TestDeviceProfile, nil), - true, - http.StatusBadRequest, - }, - { - "Duplicate command name", - createDeviceProfileRequestWithFile(dupeBody), - createDBClientWithOutlines([]mockOutline{}), - createMockValueDescriptorClient([]mockOutline{}, requestContext, TestDeviceProfile, nil), - true, - http.StatusBadRequest, - }, - { - "Duplicate profile name", - createDeviceProfileRequestWithFile(okBody), - createDBClientWithOutlines([]mockOutline{ - {"AddDeviceProfile", []interface{}{dp}, []interface{}{"", db.ErrNotUnique}}, - {"GetDeviceProfileByName", []interface{}{TestDeviceProfileName}, []interface{}{contract.DeviceProfile{}, nil}}, - }), - createMockValueDescriptorClient([]mockOutline{}, requestContext, TestDeviceProfile, nil), - true, - http.StatusConflict, - }, - { - "Empty device profile name", - createDeviceProfileRequestWithFile(emptyBody), - createDBClientWithOutlines([]mockOutline{ - {"AddDeviceProfile", []interface{}{emptyName}, []interface{}{"", db.ErrNameEmpty}}, - {"GetDeviceProfileByName", []interface{}{""}, []interface{}{contract.DeviceProfile{}, db.ErrNotFound}}, - }), - createMockValueDescriptorClient([]mockOutline{ - {"Add", []interface{}{TestDeviceProfileValidated}, []interface{}{}}, - }, requestContext, TestDeviceProfile, nil), - true, - http.StatusBadRequest, - }, - { - "Unsuccessful database call", - createDeviceProfileRequestWithFile(okBody), - createDBClientWithOutlines([]mockOutline{ - {"AddDeviceProfile", []interface{}{dp}, []interface{}{"", TestError}}, - {"GetDeviceProfileByName", []interface{}{TestDeviceProfileName}, []interface{}{contract.DeviceProfile{}, db.ErrNotFound}}, - }), - createMockValueDescriptorClient([]mockOutline{ - {"Add", []interface{}{TestDeviceProfileValidated}, []interface{}{}}, - }, requestContext, TestDeviceProfile, nil), - true, - http.StatusInternalServerError, - }, - { - "Add device profile without enable value descriptor management", - createDeviceProfileRequestWithFile(okBody), - createDBClientWithOutlines([]mockOutline{ - {"AddDeviceProfile", []interface{}{dp}, []interface{}{TestDeviceProfileID, nil}}, - {"GetDeviceProfileByName", []interface{}{TestDeviceProfileName}, []interface{}{contract.DeviceProfile{}, db.ErrNotFound}}, - }), - createMockValueDescriptorClient([]mockOutline{}, requestContext, TestDeviceProfile, nil), - false, - http.StatusOK, - }, - { - "Resource operation's deviceResource is empty", - createDeviceProfileRequestWithFile(emptyDeviceResourceBody), - createDBClientWithOutlines([]mockOutline{}), - createMockValueDescriptorClient([]mockOutline{}, requestContext, TestDeviceProfile, nil), - true, - http.StatusBadRequest, - }, - { - "Core command name is empty", - createDeviceProfileRequestWithFile(emptyCoreCmdNameBody), - createDBClientWithOutlines([]mockOutline{}), - createMockValueDescriptorClient([]mockOutline{}, requestContext, TestDeviceProfile, nil), - true, - http.StatusBadRequest, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - configuration := metadataConfig.ConfigurationStruct{ - Writable: metadataConfig.WritableInfo{EnableValueDescriptorManagement: tt.enableValueDescriptorManagement}, - Service: bootstrapConfig.ServiceInfo{MaxResultCount: 1}, - } - restAddProfileByYaml( - rr, tt.request, loggerMock, tt.dbMock, errorconcept.NewErrorHandler(logger.NewMockClient()), tt.vdcMock, &configuration) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - tt.vdcMock.AssertExpectations(t) - }) - } -} - -func TestAddProfileByYamlRaw(t *testing.T) { - // we have to overwrite the CoreCommands so that their isValidated is false - dp := TestDeviceProfileValidated - - okBody, _ := yaml.Marshal(dp) - - duplicateCommandName := dp - duplicateCommandName.CoreCommands = []contract.Command{TestCommand, TestCommand} - dupeBody, _ := yaml.Marshal(duplicateCommandName) - - emptyName := dp - emptyName.Name = "" - emptyBody, _ := yaml.Marshal(emptyName) - - emptyDeviceResource := createTestDeviceProfile() - emptyDeviceResource.DeviceCommands[0].Get = []contract.ResourceOperation{ - {Index: "test index", Operation: "test operation", DeviceResource: ""}, - } - emptyDeviceResourceBody, _ := yaml.Marshal(emptyDeviceResource) - - emptyCoreCmdName := createTestDeviceProfile() - emptyCoreCmdName.CoreCommands[0].Name = "" - emptyCoreCmdNameBody, _ := yaml.Marshal(emptyCoreCmdName) - - requestContext := httptest.NewRequest(http.MethodPost, AddressableTestURI, nil).Context() - - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - vdcMock MockValueDescriptorClient - enableValueDescriptorManagement bool - expectedStatus int - }{ - { - "OK", - httptest.NewRequest(http.MethodPut, AddressableTestURI, bytes.NewBuffer(okBody)), - createDBClientWithOutlines([]mockOutline{ - {"AddDeviceProfile", []interface{}{dp}, []interface{}{TestDeviceProfileID, nil}}, - {"GetDeviceProfileByName", []interface{}{TestDeviceProfileName}, []interface{}{contract.DeviceProfile{}, db.ErrNotFound}}, - }), - createMockValueDescriptorClient([]mockOutline{ - {"Add", []interface{}{TestDeviceProfileValidated}, []interface{}{}}, - }, requestContext, TestDeviceProfile, nil), - true, - http.StatusOK, - }, - { - "Duplicate command name", - httptest.NewRequest(http.MethodPut, AddressableTestURI, bytes.NewBuffer(dupeBody)), - nil, - createMockValueDescriptorClient([]mockOutline{}, requestContext, TestDeviceProfile, nil), - true, - http.StatusBadRequest, - }, - { - "Empty device profile name", - httptest.NewRequest(http.MethodPut, AddressableTestURI, bytes.NewBuffer(emptyBody)), - createDBClientWithOutlines([]mockOutline{ - {"AddDeviceProfile", []interface{}{emptyName}, []interface{}{"", db.ErrNameEmpty}}, - {"GetDeviceProfileByName", []interface{}{""}, []interface{}{contract.DeviceProfile{}, db.ErrNotFound}}, - }), - createMockValueDescriptorClient([]mockOutline{ - {"Add", []interface{}{TestDeviceProfileValidated}, []interface{}{}}, - }, requestContext, TestDeviceProfile, nil), - true, - http.StatusBadRequest, - }, - { - "Unsuccessful database call", - httptest.NewRequest(http.MethodPut, AddressableTestURI, bytes.NewBuffer(okBody)), - createDBClientWithOutlines([]mockOutline{ - {"AddDeviceProfile", []interface{}{dp}, []interface{}{"", TestError}}, - {"GetDeviceProfileByName", []interface{}{TestDeviceProfileName}, []interface{}{contract.DeviceProfile{}, db.ErrNotFound}}, - }), - createMockValueDescriptorClient([]mockOutline{ - {"Add", []interface{}{TestDeviceProfileValidated}, []interface{}{}}, - }, requestContext, TestDeviceProfile, nil), - true, - http.StatusInternalServerError, - }, - { - "Add device profile without enable value descriptor management", - httptest.NewRequest(http.MethodPut, AddressableTestURI, bytes.NewBuffer(okBody)), - createDBClientWithOutlines([]mockOutline{ - {"AddDeviceProfile", []interface{}{dp}, []interface{}{TestDeviceProfileID, nil}}, - {"GetDeviceProfileByName", []interface{}{TestDeviceProfileName}, []interface{}{contract.DeviceProfile{}, db.ErrNotFound}}, - }), - createMockValueDescriptorClient([]mockOutline{}, requestContext, TestDeviceProfile, nil), - false, - http.StatusOK, - }, - { - "Resource operation's deviceResource is empty", - httptest.NewRequest(http.MethodPut, AddressableTestURI, bytes.NewBuffer(emptyDeviceResourceBody)), - createDBClientWithOutlines([]mockOutline{}), - createMockValueDescriptorClient([]mockOutline{}, requestContext, TestDeviceProfile, nil), - true, - http.StatusBadRequest, - }, - { - "Core command name is empty", - httptest.NewRequest(http.MethodPut, AddressableTestURI, bytes.NewBuffer(emptyCoreCmdNameBody)), - createDBClientWithOutlines([]mockOutline{}), - createMockValueDescriptorClient([]mockOutline{}, requestContext, TestDeviceProfile, nil), - true, - http.StatusBadRequest, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - configuration := metadataConfig.ConfigurationStruct{ - Writable: metadataConfig.WritableInfo{EnableValueDescriptorManagement: tt.enableValueDescriptorManagement}, - Service: bootstrapConfig.ServiceInfo{MaxResultCount: 1}, - } - restAddProfileByYamlRaw(rr, tt.request, loggerMock, tt.dbMock, errorconcept.NewErrorHandler(logger.NewMockClient()), tt.vdcMock, &configuration) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - tt.vdcMock.AssertExpectations(t) - }) - } -} - -func createDeviceProfileRequestWithFile(fileContents []byte) *http.Request { - body := new(bytes.Buffer) - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("file", "deviceProfile.yaml") - if err != nil { - return nil - } - _, err = part.Write(fileContents) - if err != nil { - return nil - } - boundary := writer.Boundary() - - err = writer.Close() - if err != nil { - return nil - } - - req, _ := http.NewRequest(http.MethodPost, AddressableTestURI, body) - req.Header.Set(clients.ContentType, "multipart/form-data; boundary="+boundary) - return req -} - -func createRequestWithBody(method string, d contract.DeviceProfile) *http.Request { - body, err := json.Marshal(d) - if err != nil { - panic("Failed to create test JSON:" + err.Error()) - } - - return httptest.NewRequest(method, AddressableTestURI, bytes.NewBuffer(body)) -} - -func createRequestWithPathParameters(httpMethod string, params map[string]string) *http.Request { - req := httptest.NewRequest(httpMethod, AddressableTestURI, nil) - return mux.SetURLVars(req, params) -} - -func createRequestWithInvalidBody() *http.Request { - return httptest.NewRequest(http.MethodPut, AddressableTestURI, bytes.NewBufferString("Bad JSON")) -} - -func createDBClient() interfaces.DBClient { - d := &mocks.DBClient{} - d.On("GetAllDeviceProfiles").Return(TestDeviceProfiles, nil) - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(TestDeviceProfile, nil) - d.On("GetDeviceProfileByName", TestDeviceProfileName).Return(TestDeviceProfile, nil) - d.On("GetDeviceProfilesByModel", TestDeviceProfile.Model).Return(TestDeviceProfiles, nil) - d.On("GetDeviceProfilesWithLabel", TestDeviceProfileLabel1).Return(TestDeviceProfiles, nil) - d.On("GetDeviceProfilesWithLabel", TestDeviceProfileLabel2).Return(TestDeviceProfiles, nil) - d.On("GetDeviceProfilesByManufacturer", TestDeviceProfile.Manufacturer).Return(TestDeviceProfiles, nil) - d.On("GetDeviceProfilesByManufacturerModel", TestDeviceProfile.Manufacturer, TestDeviceProfile.Model).Return(TestDeviceProfiles, nil) - d.On("GetDevicesByProfileId", TestDeviceProfileID).Return(make([]contract.Device, 0), nil) - - // Methods which need to return empty slices so that the business logic does not return a conflict due to the - // DeviceProfile being in use. This is for update and deletion functionality. - d.On("GetProvisionWatchersByProfileId", TestDeviceProfileID).Return(make([]contract.ProvisionWatcher, 0), nil) - d.On("UpdateDeviceProfile", mock.Anything).Return(nil) - d.On("DeleteDeviceProfileById", TestDeviceProfileID).Return(nil) - d.On("DeleteDeviceProfileByName", TestDeviceProfileName).Return(nil) - - return d -} - -func createDBClientDeviceProfileErrorNotFound() interfaces.DBClient { - d := &mocks.DBClient{} - d.On("GetDeviceProfileByName", TestDeviceProfileName).Return(contract.DeviceProfile{}, db.ErrNotFound) - d.On("GetAllDeviceProfiles").Return(nil, db.ErrNotFound) - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(contract.DeviceProfile{}, db.ErrNotFound) - d.On("GetDeviceProfilesByModel", TestDeviceProfile.Model).Return(nil, db.ErrNotFound) - d.On("GetDeviceProfilesWithLabel", TestDeviceProfileLabel1).Return(nil, db.ErrNotFound) - d.On("GetDeviceProfilesWithLabel", TestDeviceProfileLabel2).Return(nil, db.ErrNotFound) - d.On("GetDeviceProfilesByManufacturer", TestDeviceProfile.Manufacturer).Return(nil, db.ErrNotFound) - d.On("GetDeviceProfilesByManufacturerModel", TestDeviceProfile.Manufacturer, TestDeviceProfile.Model).Return(nil, db.ErrNotFound) - - return d -} - -func createMockErrDeviceProfileNotFound() interfaces.DBClient { - d := &mocks.DBClient{} - d.On("GetDeviceProfileByName", TestDeviceProfileName).Return(contract.DeviceProfile{}, errors.ErrDeviceProfileNotFound{}) - d.On("GetDeviceProfileById", TestDeviceProfileName).Return(contract.DeviceProfile{}, errors.ErrDeviceProfileNotFound{}) - d.On("GetDeviceProfileByName", TestDeviceProfileID).Return(contract.DeviceProfile{}, errors.ErrDeviceProfileNotFound{}) - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(contract.DeviceProfile{}, errors.ErrDeviceProfileNotFound{}) - - return d -} - -func createDBClientMultipleDevicesFoundError() interfaces.DBClient { - d := &mocks.DBClient{} - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(TestDeviceProfile, nil) - d.On("GetDeviceProfileByName", TestDeviceProfileName).Return(TestDeviceProfile, nil) - d.On("GetDevicesByProfileId", TestDeviceProfileID).Return(TestDevices, nil) - - return d -} - -func createDBClientMultipleProvisionWatchersFoundError() interfaces.DBClient { - d := &mocks.DBClient{} - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(TestDeviceProfile, nil) - d.On("GetDeviceProfileByName", TestDeviceProfileName).Return(TestDeviceProfile, nil) - d.On("GetDeviceProfileByName", TestDeviceProfileName).Return(contract.DeviceProfile{}, TestError) - d.On("GetDevicesByProfileId", TestDeviceProfileID).Return(make([]contract.Device, 0), nil) - d.On("GetProvisionWatchersByProfileId", TestDeviceProfileID).Return(TestProvisionWatchers, nil) - - return d -} - -func createDBClientGetDevicesByProfileIdError() interfaces.DBClient { - d := &mocks.DBClient{} - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(TestDeviceProfile, nil) - d.On("GetDeviceProfileByName", TestDeviceProfileName).Return(TestDeviceProfile, nil) - d.On("GetDevicesByProfileId", TestDeviceProfileID).Return(make([]contract.Device, 0), TestError) - - return d -} - -func createDBClientGetProvisionWatchersByProfileIdError() interfaces.DBClient { - d := &mocks.DBClient{} - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(TestDeviceProfile, nil) - d.On("GetDeviceProfileByName", TestDeviceProfileName).Return(TestDeviceProfile, nil) - d.On("GetDevicesByProfileId", TestDeviceProfileID).Return(make([]contract.Device, 0), nil) - d.On("GetProvisionWatchersByProfileId", TestDeviceProfileID).Return(make([]contract.ProvisionWatcher, 0), TestError) - d.On("GetAllDeviceProfiles").Return([]contract.DeviceProfile{}, TestError) - - return d -} - -func createDBClientPersistDeviceProfileError() interfaces.DBClient { - d := &mocks.DBClient{} - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(TestDeviceProfile, nil) - d.On("GetDeviceProfileByName", TestDeviceProfileName).Return(TestDeviceProfile, nil) - d.On("GetDevicesByProfileId", TestDeviceProfileID).Return(make([]contract.Device, 0), nil) - d.On("GetProvisionWatchersByProfileId", TestDeviceProfileID).Return(make([]contract.ProvisionWatcher, 0), nil) - d.On("GetAllDeviceProfiles").Return(make([]contract.DeviceProfile, 0), nil) - - // Mock both persistence functions so this can be used for updates and delete tests - d.On("UpdateDeviceProfile", mock.Anything).Return(TestError) - d.On("DeleteDeviceProfileById", mock.Anything).Return(TestError) - - return d -} - -func createDBClientGetDeviceProfileError() interfaces.DBClient { - d := &mocks.DBClient{} - d.On("GetAllDeviceProfiles").Return(nil, TestError) - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(contract.DeviceProfile{}, TestError) - d.On("GetDeviceProfileByName", TestDeviceProfileName).Return(contract.DeviceProfile{}, TestError) - d.On("GetDeviceProfilesByModel", TestDeviceProfile.Model).Return(nil, TestError) - d.On("GetDeviceProfilesWithLabel", TestDeviceProfileLabel1).Return(nil, TestError) - d.On("GetDeviceProfilesWithLabel", TestDeviceProfileLabel2).Return(nil, TestError) - d.On("GetDeviceProfilesByManufacturer", TestDeviceProfile.Manufacturer).Return(nil, TestError) - d.On("GetDeviceProfilesByManufacturerModel", TestDeviceProfile.Manufacturer, TestDeviceProfile.Model).Return(nil, TestError) - - return d -} - -func createDBClientGetDeviceProfileMaxLimitError() interfaces.DBClient { - d := &mocks.DBClient{} - d.On("GetAllDeviceProfiles").Return(nil, errors.ErrLimitExceeded{}) - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(contract.DeviceProfile{}, errors.ErrLimitExceeded{}) - d.On("GetDeviceProfileByName", TestDeviceProfileName).Return(contract.DeviceProfile{}, errors.ErrLimitExceeded{}) - d.On("GetDeviceProfilesByModel", TestDeviceProfile.Model).Return(nil, errors.ErrLimitExceeded{}) - d.On("GetDeviceProfilesWithLabel", TestDeviceProfileLabel1).Return(nil, errors.ErrLimitExceeded{}) - d.On("GetDeviceProfilesWithLabel", TestDeviceProfileLabel2).Return(nil, errors.ErrLimitExceeded{}) - d.On("GetDeviceProfilesByManufacturer", TestDeviceProfile.Manufacturer).Return(nil, errors.ErrLimitExceeded{}) - d.On("GetDeviceProfilesByManufacturerModel", TestDeviceProfile.Manufacturer, TestDeviceProfile.Model).Return(nil, errors.ErrLimitExceeded{}) - - return d -} - -func createDBClientPersistDeviceInUseError() interfaces.DBClient { - d := &mocks.DBClient{} - inUse := TestDeviceProfile - inUse.DeviceResources = append(inUse.DeviceResources, TestInUseDeviceResource) - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(inUse, nil) - d.On("GetDeviceProfileByName", TestDeviceProfileName).Return(inUse, nil) - d.On("GetDevicesByProfileId", TestDeviceProfileID).Return([]contract.Device{}, nil) - - return d -} - -func createDBClientUpdateValueDescriptorError() interfaces.DBClient { - d := &mocks.DBClient{} - inUse := TestDeviceProfile - inUse.DeviceResources = append(inUse.DeviceResources, TestInUseDeviceResource) - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(contract.DeviceProfile{}, TestError) - d.On("GetDeviceProfileByName", TestDeviceProfileName).Return(contract.DeviceProfile{}, TestError) - - return d -} - -func createDBServiceClientError(statusCode int) interfaces.DBClient { - d := &mocks.DBClient{} - inUse := TestDeviceProfile - inUse.DeviceResources = append(inUse.DeviceResources, TestInUseDeviceResource) - d.On("GetDeviceProfileById", TestDeviceProfileID).Return(contract.DeviceProfile{}, types.ErrServiceClient{ - StatusCode: statusCode, - }) - d.On("GetDeviceProfileByName", TestDeviceProfileName).Return(contract.DeviceProfile{}, types.ErrServiceClient{ - StatusCode: statusCode, - }) - - return d -} - -func createDBClientWithOutlines(outlines []mockOutline) interfaces.DBClient { - dbMock := mocks.DBClient{} - - for _, o := range outlines { - dbMock.On(o.methodName, o.arg...).Return(o.ret...) - } - - return &dbMock -} - -func createMockValueDescriptorClient(outlines []mockOutline, requestContext context.Context, deviceProfile contract.DeviceProfile, err error) MockValueDescriptorClient { - mockClient := MockValueDescriptorClient{&mock.Mock{}, err} - - for _, o := range outlines { - for _, dr := range deviceProfile.DeviceResources { - vd := contract.From(dr) - mockClient.On(o.methodName, requestContext, &vd).Return("", err).Once() - // Value descriptor client occur error, so only call the add method once. - if mockClient.errorToThrow != nil { - break - } - } - } - - return mockClient -} - -// createTestDeviceProfile creates a device profile to be used during testing. -// This function handles some of the necessary creation nuances which need to take place for proper mocking and equality -// verifications. -func createTestDeviceProfile() contract.DeviceProfile { - return createTestDeviceProfileWithCommands(TestDeviceProfileID, TestDeviceProfileName, TestDeviceProfileLabels, TestDeviceProfileManufacturer, TestDeviceProfileModel, TestCommand) -} - -// createValidatedTestDeviceProfile creates an object by deserializing it from JSON -// so that its unexported field isValidated will be true. -func createValidatedTestDeviceProfile() contract.DeviceProfile { - bytes, _ := json.Marshal(TestDeviceProfile) - var dp contract.DeviceProfile - _ = json.Unmarshal(bytes, &dp) - - return dp -} - -// createTestDeviceProfileWithCommands creates a device profile to be used during testing. -// This function handles some of the necessary creation nuances which need to take place for proper mocking and equality -// verifications. -func createTestDeviceProfileWithCommands( - id string, - name string, - labels []string, - manufacturer string, - model string, - commands ...contract.Command) contract.DeviceProfile { - - return contract.DeviceProfile{ - Id: id, - Name: name, - DescribedObject: contract.DescribedObject{ - Description: "Some test data", - Timestamps: contract.Timestamps{ - Origin: 123, - Created: 456, - Modified: 789, - }, - }, - Labels: labels, - Manufacturer: manufacturer, - Model: model, - CoreCommands: createCoreCommands(commands), - DeviceResources: []contract.DeviceResource{ - TestDeleteDeviceResource, - TestUpdateDeviceResource, - }, - DeviceCommands: []contract.ProfileResource{ - { - Name: "TestProfileResource", - }, - }, - } -} - -// createCoreCommands creates Command instances which can be used during testing. -// This function is necessary due to the internal field 'isValidated', which is not exported, being false when created -// manually and true when serialized. This causes the mocking infrastructure to not match when Commands are involved -// with matching parameters or verifying results. -func createCoreCommands(commands []contract.Command) []contract.Command { - cs := make([]contract.Command, 0) - for _, command := range commands { - b, _ := command.MarshalJSON() - var temp contract.Command - err := json.Unmarshal(b, &temp) - if err != nil { - panic(err.Error()) - } - - cs = append(cs, temp) - } - - return cs -} - -type MockValueDescriptorClient struct { - *mock.Mock - errorToThrow error -} - -func (MockValueDescriptorClient) ValueDescriptorsUsage(ctx context.Context, names []string) (map[string]bool, error) { - usage := map[string]bool{} - for _, n := range names { - if n == TestInUseDeviceResource.Name { - usage[n] = true - continue - } - - usage[n] = false - } - return usage, nil -} - -func (mvdc MockValueDescriptorClient) Add(ctx context.Context, vdr *contract.ValueDescriptor) (string, error) { - mvdc.Called(ctx, vdr) - if mvdc.errorToThrow != nil { - return "", mvdc.errorToThrow - } - - return "", nil -} - -func (MockValueDescriptorClient) Update(ctx context.Context, vdr *contract.ValueDescriptor) error { - return nil -} - -func (MockValueDescriptorClient) DeleteByName(ctx context.Context, name string) error { - return nil -} - -func (MockValueDescriptorClient) ValueDescriptorForName(ctx context.Context, name string) (contract.ValueDescriptor, error) { - return contract.ValueDescriptor{Id: name}, nil -} - -func (MockValueDescriptorClient) ValueDescriptors(ctx context.Context) ([]contract.ValueDescriptor, error) { - panic("not expected to be invoked") -} - -func (MockValueDescriptorClient) ValueDescriptor(ctx context.Context, id string) (contract.ValueDescriptor, error) { - panic("not expected to be invoked") -} - -func (MockValueDescriptorClient) ValueDescriptorsByLabel(ctx context.Context, label string) ([]contract.ValueDescriptor, error) { - panic("not expected to be invoked") -} - -func (MockValueDescriptorClient) ValueDescriptorsForDevice(ctx context.Context, deviceId string) ([]contract.ValueDescriptor, error) { - panic("not expected to be invoked") -} - -func (MockValueDescriptorClient) ValueDescriptorsForDeviceByName(ctx context.Context, deviceName string) ([]contract.ValueDescriptor, error) { - panic("not expected to be invoked") -} - -func (MockValueDescriptorClient) ValueDescriptorsByUomLabel(ctx context.Context, uomLabel string) ([]contract.ValueDescriptor, error) { - panic("not expected to be invoked") -} - -func (MockValueDescriptorClient) Delete(ctx context.Context, id string) error { - panic("not expected to be invoked") -} diff --git a/internal/core/metadata/rest_devicereport.go b/internal/core/metadata/rest_devicereport.go deleted file mode 100644 index 502009d61f..0000000000 --- a/internal/core/metadata/rest_devicereport.go +++ /dev/null @@ -1,399 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * - * 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 metadata - -import ( - "encoding/json" - "net/http" - "net/url" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/config" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" -) - -func restGetAllDeviceReports( - w http.ResponseWriter, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - res, err := dbClient.GetAllDeviceReports() - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusInternalServer) - return - } - - // Check max limit - if err = checkMaxLimit(len(res), configuration); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.LimitExceeded) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - json.NewEncoder(w).Encode(&res) -} - -// Add a new device report -// Referenced objects (Device, Schedule event) must already exist -// 404 If any of the referenced objects aren't found -func restAddDeviceReport( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - defer r.Body.Close() - var dr models.DeviceReport - if err := json.NewDecoder(r.Body).Decode(&dr); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the device exists - if _, err := dbClient.GetDeviceByName(dr.Device); err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.DeviceReport.DeviceNotFound, - errorconcept.Default.InternalServerError) - return - } - - // Add the device report - var err error - dr.Id, err = dbClient.AddDeviceReport(dr) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.DeviceReport.NotUnique, - errorconcept.Default.InternalServerError) - return - } - - // Notify associates - if err := notifyDeviceReportAssociates(dr, http.MethodPost, lc, dbClient); err != nil { - lc.Error(err.Error()) - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte(dr.Id)) -} - -func restUpdateDeviceReport( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - defer r.Body.Close() - var from models.DeviceReport - if err := json.NewDecoder(r.Body).Decode(&from); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the device report exists - // First try ID - to, err := dbClient.GetDeviceReportById(from.Id) - if err != nil { - // Try by name - to, err = dbClient.GetDeviceReportByName(from.Name) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Database.NotFound, - errorconcept.Default.InternalServerError) - return - } - } - - if err := updateDeviceReportFields(from, &to, w, dbClient, errorHandler); err != nil { - lc.Error(err.Error()) - return - } - - if err := dbClient.UpdateDeviceReport(to); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.UpdateError_StatusInternalServer) - return - } - - // Notify Associates - if err := notifyDeviceReportAssociates(to, http.MethodPut, lc, dbClient); err != nil { - lc.Error(err.Error()) - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -// Update the relevant fields for the device report -func updateDeviceReportFields( - from models.DeviceReport, - to *models.DeviceReport, - w http.ResponseWriter, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) error { - - if from.Device != "" { - to.Device = from.Device - if err := validateDevice(to.Device, w, dbClient, errorHandler); err != nil { - return err - } - } - if from.Action != "" { - to.Action = from.Action - } - if from.Expected != nil { - to.Expected = from.Expected - // TODO: Someday find a way to check the value descriptors - } - if from.Name != "" { - to.Name = from.Name - } - if from.Origin != 0 { - to.Origin = from.Origin - } - - return nil -} - -// Validate that the device exists -func validateDevice( - d string, - w http.ResponseWriter, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) error { - - if _, err := dbClient.GetDeviceByName(d); err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.DeviceReport.DeviceNotFound, - errorconcept.Default.ServiceUnavailable) - return err - } - - return nil -} - -func restGetReportById( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var did string = vars[ID] - res, err := dbClient.GetDeviceReportById(did) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - json.NewEncoder(w).Encode(res) -} - -func restGetReportByName( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - res, err := dbClient.GetDeviceReportByName(n) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - json.NewEncoder(w).Encode(res) -} - -// Get a list of value descriptor names -// The names are a union of all the value descriptors from the device reports for the given device -func restGetValueDescriptorsForDeviceName( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[DEVICENAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Get all the associated device reports - reports, err := dbClient.GetDeviceReportByDeviceName(n) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusInternalServer) - return - } - - valueDescriptors := []string{} - for _, report := range reports { - for _, e := range report.Expected { - valueDescriptors = append(valueDescriptors, e) - } - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - json.NewEncoder(w).Encode(valueDescriptors) -} -func restGetDeviceReportByDeviceName( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[DEVICENAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - res, err := dbClient.GetDeviceReportByDeviceName(n) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusInternalServer) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - json.NewEncoder(w).Encode(res) -} - -func restDeleteReportById( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var id string = vars[ID] - - // Check if the device report exists - dr, err := dbClient.GetDeviceReportById(id) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.InternalServerError) - return - } - - if err := deleteDeviceReport(dr, w, lc, dbClient, errorHandler); err != nil { - lc.Error(err.Error()) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restDeleteReportByName( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the device report exists - dr, err := dbClient.GetDeviceReportByName(n) - if err != nil { - errorHandler.HandleOneVariant(w, err, errorconcept.Database.NotFound, errorconcept.Default.InternalServerError) - return - } - - if err = deleteDeviceReport(dr, w, lc, dbClient, errorHandler); err != nil { - lc.Error(err.Error()) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func deleteDeviceReport( - dr models.DeviceReport, - w http.ResponseWriter, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) error { - - if err := dbClient.DeleteDeviceReportById(dr.Id); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.DeleteError) - return err - } - - // Notify Associates - if err := notifyDeviceReportAssociates(dr, http.MethodDelete, lc, dbClient); err != nil { - return err - } - - return nil -} - -// Notify the associated device services to the device report -func notifyDeviceReportAssociates( - dr models.DeviceReport, - action string, - lc logger.LoggingClient, - dbClient interfaces.DBClient) error { - - // Get the device of the report - d, err := dbClient.GetDeviceByName(dr.Device) - if err != nil { - return err - } - - // Get the device service for the device - ds, err := dbClient.GetDeviceServiceById(d.Service.Id) - if err != nil { - return err - } - - // Notify the associating device services - if err = notifyAssociates([]models.DeviceService{ds}, dr.Id, action, models.REPORT, lc); err != nil { - return err - } - - return nil -} diff --git a/internal/core/metadata/rest_deviceservice.go b/internal/core/metadata/rest_deviceservice.go deleted file mode 100644 index ac22ca61b8..0000000000 --- a/internal/core/metadata/rest_deviceservice.go +++ /dev/null @@ -1,843 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 metadata - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "net/http" - "net/url" - "strconv" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/config" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/operators/device_service" - "github.com/edgexfoundry/edgex-go/internal/pkg" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/notifications" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" -) - -func restGetAllDeviceServices( - w http.ResponseWriter, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - op := device_service.NewDeviceServiceLoadAll(configuration.Service, dbClient, lc) - services, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.LimitExceeded, - errorconcept.Default.InternalServerError) - return - } - pkg.Encode(services, w, lc) -} - -func restAddDeviceService( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - defer r.Body.Close() - var ds models.DeviceService - err := json.NewDecoder(r.Body).Decode(&ds) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Addressable Check - // No ID or Name given for addressable - if ds.Addressable.Id == "" && ds.Addressable.Name == "" { - errorHandler.Handle(w, err, errorconcept.DeviceService.EmptyAddressable) - return - } - - // First try by name - addressable, err := dbClient.GetAddressableByName(ds.Addressable.Name) - if err != nil && err == db.ErrNotFound && ds.Addressable.Id != "" { - addressable, err = dbClient.GetAddressableById(ds.Addressable.Id) - } - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.DeviceService.AddressableNotFound, - errorconcept.Default.InternalServerError) - return - } - ds.Addressable = addressable - - // Add the device service - if ds.Id, err = dbClient.AddDeviceService(ds); err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.DeviceService.NotUnique, - errorconcept.Default.InternalServerError) - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte(ds.Id)) -} - -func restUpdateDeviceService( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - defer r.Body.Close() - var from models.DeviceService - err := json.NewDecoder(r.Body).Decode(&from) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the device service exists and get it - var to models.DeviceService - // Try by ID - if from.Id != "" { - to, err = dbClient.GetDeviceServiceById(from.Id) - } - if from.Id == "" || err != nil { - // Try by Name - if to, err = dbClient.GetDeviceServiceByName(from.Name); err != nil { - errorHandler.Handle(w, err, errorconcept.DeviceService.NotFound) - return - } - } - - if err = updateDeviceServiceFields(from, &to, w, dbClient, errorHandler); err != nil { - lc.Error(err.Error()) - return - } - - if err := dbClient.UpdateDeviceService(to); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.UpdateError_StatusInternalServer) - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -// Update the relevant device service fields -func updateDeviceServiceFields( - from models.DeviceService, - to *models.DeviceService, - w http.ResponseWriter, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) error { - - // Use .String() to compare empty structs (not ideal, but there is no .equals method) - if (from.Addressable.String() != models.Addressable{}.String()) { - var addr models.Addressable - var err error - if from.Addressable.Id != "" { - addr, err = dbClient.GetAddressableById(from.Addressable.Id) - } - if from.Addressable.Id == "" || err != nil { - addr, err = dbClient.GetAddressableByName(from.Addressable.Name) - if err != nil { - return err - } - } - to.Addressable = addr - } - - to.AdminState = from.AdminState - if from.Description != "" { - to.Description = from.Description - } - if from.Labels != nil { - to.Labels = from.Labels - } - if from.LastConnected != 0 { - to.LastConnected = from.LastConnected - } - if from.LastReported != 0 { - to.LastReported = from.LastReported - } - if from.Name != "" { - to.Name = from.Name - - // Check if the new name is unique - checkDS, err := dbClient.GetDeviceServiceByName(from.Name) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.NewDeviceServiceDuplicate(checkDS.Id, to.Id), - errorconcept.Default.ServiceUnavailable) - } - } - - to.OperatingState = from.OperatingState - if from.Origin != 0 { - to.Origin = from.Origin - } - - return nil -} - -func restGetServiceByAddressableName( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - an, err := url.QueryUnescape(vars[ADDRESSABLENAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - op := device_service.NewDeviceServiceLoadByAddressableName(an, dbClient) - res, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.ItemNotFound, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - json.NewEncoder(w).Encode(res) -} - -func restGetServiceByAddressableId( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var sid = vars[ADDRESSABLEID] - - op := device_service.NewDeviceServiceLoadByAddressableID(sid, dbClient) - res, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.ItemNotFound, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - json.NewEncoder(w).Encode(res) -} - -func restGetServiceWithLabel( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - l, err := url.QueryUnescape(vars[LABEL]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - res := make([]models.DeviceService, 0) - if res, err = dbClient.GetDeviceServicesWithLabel(l); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusInternalServer) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - json.NewEncoder(w).Encode(res) -} - -func restGetServiceByName( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - dn, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - op := device_service.NewDeviceServiceLoadByName(dn, dbClient) - res, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.ItemNotFound, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - json.NewEncoder(w).Encode(res) -} - -func restDeleteServiceById( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) { - - vars := mux.Vars(r) - var id string = vars[ID] - - // Check if the device service exists and get it - ds, err := dbClient.GetDeviceServiceById(id) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.DeviceService.NotFound, - errorconcept.Default.InternalServerError) - return - } - - ctx := r.Context() - if err = deleteDeviceService(ds, w, ctx, lc, dbClient, errorHandler, nc, configuration); err != nil { - lc.Error(err.Error()) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.Write([]byte("true")) -} - -func restDeleteServiceByName( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the device service exists - ds, err := dbClient.GetDeviceServiceByName(n) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.DeviceService.NotFound, - errorconcept.Default.InternalServerError) - return - } - - ctx := r.Context() - // Delete the device service - if err = deleteDeviceService(ds, w, ctx, lc, dbClient, errorHandler, nc, configuration); err != nil { - lc.Error(err.Error()) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -// Delete the device service -// Delete the associated devices -// Delete the associated provision watchers -func deleteDeviceService( - ds models.DeviceService, - w http.ResponseWriter, - ctx context.Context, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - nc notifications.NotificationsClient, - configuration *config.ConfigurationStruct) error { - - // Delete the associated devices - devices, err := dbClient.GetDevicesByServiceId(ds.Id) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusServiceUnavailable) - return err - } - for _, device := range devices { - if err = deleteDevice(device, w, ctx, lc, dbClient, errorHandler, nc, configuration); err != nil { - return err - } - } - - // Delete the associated provision watchers - watchers, err := dbClient.GetProvisionWatchersByServiceId(ds.Id) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusServiceUnavailable) - return err - } - for _, watcher := range watchers { - if err = deleteProvisionWatcher(watcher, w, lc, dbClient, errorHandler); err != nil { - return err - } - } - - // Delete the device service - if err = dbClient.DeleteDeviceServiceById(ds.Id); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.DeleteError) - return err - } - - return nil -} - -func restUpdateServiceLastConnectedById( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var id string = vars[ID] - var vlc string = vars[LASTCONNECTED] - lastConnected, err := strconv.ParseInt(vlc, 10, 64) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the device service exists - ds, err := dbClient.GetDeviceServiceById(id) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.DeviceService.NotFound, - errorconcept.Default.InternalServerError) - return - } - - if err = updateServiceLastConnected(ds, lastConnected, w, dbClient, errorHandler); err != nil { - lc.Error(err.Error()) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restUpdateServiceLastConnectedByName( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - var vlc string = vars[LASTCONNECTED] - lastConnected, err := strconv.ParseInt(vlc, 10, 64) - if err != nil { - errorHandler.Handle(w, err, errorconcept.DeviceService.InvalidRequest_StatusInternalServer) - return - } - - // Check if the device service exists - ds, err := dbClient.GetDeviceServiceByName(n) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.DeviceService.NotFound, - errorconcept.Default.InternalServerError) - return - } - - // Update last connected - if err = updateServiceLastConnected(ds, lastConnected, w, dbClient, errorHandler); err != nil { - lc.Error(err.Error()) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -// Update the last connected value of the device service -func updateServiceLastConnected( - ds models.DeviceService, - lc int64, - w http.ResponseWriter, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) error { - - ds.LastConnected = lc - - if err := dbClient.UpdateDeviceService(ds); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.UpdateError_StatusServiceUnavailable) - return err - } - - return nil -} - -func restGetServiceById( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var did = vars[ID] - - op := device_service.NewDeviceServiceLoadById(did, dbClient) - res, err := op.Execute() - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.ItemNotFound, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - json.NewEncoder(w).Encode(res) -} - -func restUpdateServiceOpStateById( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var id = vars[ID] - var os = vars[OPSTATE] - - // Check the OpState - newOs, f := models.GetOperatingState(os) - if !f { - err := errors.New("Invalid State: " + os + " Must be 'ENABLED' or 'DISABLED'") - errorHandler.Handle(w, err, errorconcept.DeviceService.InvalidState) - return - } - - op := device_service.NewUpdateOpStateByIdExecutor(id, newOs, dbClient) - if err := op.Execute(); err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.ItemNotFound, - errorconcept.Default.InternalServerError) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restUpdateServiceOpStateByName( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - var os = vars[OPSTATE] - - // Check the OpState - newOs, f := models.GetOperatingState(os) - if !f { - err = errors.New("Invalid State: " + os + " Must be 'ENABLED' or 'DISABLED'") - errorHandler.Handle(w, err, errorconcept.DeviceService.InvalidState) - return - } - - op := device_service.NewUpdateOpStateByNameExecutor(n, newOs, dbClient) - if err := op.Execute(); err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.ItemNotFound, - errorconcept.Default.InternalServerError) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restUpdateServiceAdminStateById( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var id = vars[ID] - var as = vars[ADMINSTATE] - - // Check the admin state - newAs, f := models.GetAdminState(as) - if !f { - err := errors.New("Invalid state: " + as + " Must be 'LOCKED' or 'UNLOCKED'") - errorHandler.Handle(w, err, errorconcept.DeviceService.InvalidState) - return - } - - op := device_service.NewUpdateAdminStateByIdExecutor(id, newAs, dbClient, lc) - if err := op.Execute(); err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.ItemNotFound, - errorconcept.Default.InternalServerError) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restUpdateServiceAdminStateByName( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - var as = vars[ADMINSTATE] - - // Check the admin state - newAs, f := models.GetAdminState(as) - if !f { - err := errors.New("Invalid state: " + as + " Must be 'LOCKED' or 'UNLOCKED'") - errorHandler.Handle(w, err, errorconcept.DeviceService.InvalidState) - return - } - - op := device_service.NewUpdateAdminStateByNameExecutor(n, newAs, dbClient, lc) - if err := op.Execute(); err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.Common.ItemNotFound, - errorconcept.Default.InternalServerError) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restUpdateServiceLastReportedById( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var id string = vars[ID] - var vlr string = vars[LASTREPORTED] - lr, err := strconv.ParseInt(vlr, 10, 64) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the device service exists - ds, err := dbClient.GetDeviceServiceById(id) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.DeviceService.NotFound, - errorconcept.Default.InternalServerError) - return - } - - if err = updateServiceLastReported(ds, lr, w, dbClient, errorHandler); err != nil { - lc.Error(err.Error()) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restUpdateServiceLastReportedByName( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - var vlr string = vars[LASTREPORTED] - lr, err := strconv.ParseInt(vlr, 10, 64) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the device service exists - ds, err := dbClient.GetDeviceServiceByName(n) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.DeviceService.NotFound, - errorconcept.Default.InternalServerError) - return - } - - if err = updateServiceLastReported(ds, lr, w, dbClient, errorHandler); err != nil { - lc.Error(err.Error()) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -// Update the last reported value for the device service -func updateServiceLastReported( - ds models.DeviceService, - lr int64, - w http.ResponseWriter, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) error { - - ds.LastReported = lr - if err := dbClient.UpdateDeviceService(ds); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.UpdateError_StatusServiceUnavailable) - return err - } - - return nil -} - -// Notify associates (associated device services) -// This function is called when an object changes in metadata -func notifyAssociates( - deviceServices []models.DeviceService, - id string, - action string, - actionType models.ActionType, - lc logger.LoggingClient) error { - - for _, ds := range deviceServices { - if err := callback(ds, id, action, actionType, lc); err != nil { - return err - } - } - - return nil -} - -// Make the callback for the device service -func callback( - service models.DeviceService, - id string, - action string, - actionType models.ActionType, - lc logger.LoggingClient) error { - - client := &http.Client{} - url := service.Addressable.GetCallbackURL() - if len(url) > 0 { - body, err := getBody(id, actionType) - if err != nil { - return err - } - req, err := http.NewRequest(string(action), url, bytes.NewReader(body)) - if err != nil { - return err - } - req.Header.Add(clients.ContentType, clients.ContentTypeJSON) - - go makeRequest(client, req, lc) - } else { - lc.Info("callback::no addressable for " + service.Name) - } - return nil -} - -// Asynchronous call -func makeRequest(client *http.Client, req *http.Request, lc logger.LoggingClient) { - // Make the request - resp, err := client.Do(req) - if err == nil { - defer resp.Body.Close() - resp.Close = true - } else { - lc.Error(err.Error()) - } -} - -// Turn the ID and ActionType into the JSON body that will be passed -func getBody(id string, actionType models.ActionType) ([]byte, error) { - return json.Marshal(models.CallbackAlert{ActionType: actionType, Id: id}) -} diff --git a/internal/core/metadata/rest_deviceservice_test.go b/internal/core/metadata/rest_deviceservice_test.go deleted file mode 100644 index 6e483d73e9..0000000000 --- a/internal/core/metadata/rest_deviceservice_test.go +++ /dev/null @@ -1,697 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 metadata - -import ( - "bytes" - "encoding/json" - "errors" - "net/http" - "net/http/httptest" - "strconv" - "testing" - - metadataConfig "github.com/edgexfoundry/edgex-go/internal/core/metadata/config" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces/mocks" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/google/uuid" - "github.com/gorilla/mux" -) - -var testDeviceServiceURI = clients.ApiBase + "/" + DEVICESERVICE -var testAddressable = contract.Addressable{Id: testDeviceServiceId, Name: testDeviceServiceName} -var testDeviceServiceId = uuid.New().String() -var testDeviceServiceName = "test service" -var testDeviceService = contract.DeviceService{Id: testDeviceServiceId, Name: testDeviceServiceName} -var testDeviceServices = []contract.DeviceService{testDeviceService} -var testOperatingState, _ = contract.GetOperatingState(contract.Enabled) -var testAdminState, _ = contract.GetAdminState(contract.Unlocked) -var testError = errors.New("some error") -var testDeviceServiceLastUpdated = contract.DeviceService{ - Id: testDeviceServiceLastUpdatedId, - Name: testDeviceServiceName, - LastReported: lastReported} -var testDeviceServiceLastUpdatedId = "b3445cc6-87df-48f4-b8b0-587dc8a4e1c2" -var testDeviceServiceLastUpdatedNotExist = contract.DeviceService{ - Id: testDeviceServiceLastUpdatedNotExistId, - Name: testDeviceServiceName, - LastReported: lastReported} -var testDeviceServiceLastUpdatedNotExistId = "0" - -const ( - lastReported = 123546789 -) - -func TestGetAllDeviceServices(t *testing.T) { - tests := []struct { - name string - dbMock interfaces.DBClient - expectedStatus int - }{ - {"OK", createGetDeviceServiceLoaderMock(1), http.StatusOK}, - {"MaxExceeded", createGetDeviceServiceLoaderMock(2), http.StatusRequestEntityTooLarge}, - {"Unexpected", createGetDeviceServiceLoaderMockFail(), http.StatusInternalServerError}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - var loggerMock = logger.NewMockClient() - configuration := metadataConfig.ConfigurationStruct{ - Service: bootstrapConfig.ServiceInfo{MaxResultCount: 1}, - } - restGetAllDeviceServices( - rr, - loggerMock, - tt.dbMock, - errorconcept.NewErrorHandler(loggerMock), - &configuration) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestGetServiceByName(t *testing.T) { - tests := []struct { - name string - dbMock interfaces.DBClient - value string - expectedStatus int - }{ - { - "OK", - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceByName", []interface{}{testDeviceServiceName}, []interface{}{testDeviceService, nil}}, - }), - testDeviceServiceName, - http.StatusOK, - }, - { - "Invalid name", - nil, - "%ERR", - http.StatusBadRequest, - }, - { - "Device service not found", - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceByName", []interface{}{testDeviceServiceName}, []interface{}{contract.DeviceService{}, db.ErrNotFound}}, - }), - testDeviceServiceName, - http.StatusNotFound, - }, - { - "Device services lookup error", - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceByName", []interface{}{testDeviceServiceName}, []interface{}{contract.DeviceService{}, testError}}, - }), - testDeviceServiceName, - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetServiceByName( - rr, - createDeviceServiceRequest(http.MethodGet, NAME, tt.value), - tt.dbMock, - errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestGetServiceById(t *testing.T) { - req := createDeviceServiceRequest(http.MethodGet, ID, testDeviceServiceId) - - tests := []struct { - name string - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceById", []interface{}{testDeviceServiceId}, []interface{}{testDeviceService, nil}}, - }), - http.StatusOK, - }, - { - "Device service not found", - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceById", []interface{}{testDeviceServiceId}, []interface{}{contract.DeviceService{}, db.ErrNotFound}}, - }), - http.StatusNotFound, - }, - { - "Device services lookup error", - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceById", []interface{}{testDeviceServiceId}, []interface{}{contract.DeviceService{}, testError}}, - }), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetServiceById(rr, req, tt.dbMock, errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestGetServiceByAddressableName(t *testing.T) { - tests := []struct { - name string - dbMock interfaces.DBClient - value string - expectedStatus int - }{ - { - "OK", - createMockWithOutlines([]mockOutline{ - {"GetAddressableByName", []interface{}{testDeviceServiceName}, []interface{}{testAddressable, nil}}, - {"GetDeviceServicesByAddressableId", []interface{}{testDeviceServiceId}, []interface{}{testDeviceServices, nil}}, - }), - testDeviceServiceName, - http.StatusOK, - }, - { - "Addressable not found", - createMockWithOutlines([]mockOutline{ - {"GetAddressableByName", []interface{}{testDeviceServiceName}, []interface{}{contract.Addressable{}, db.ErrNotFound}}, - }), - testDeviceServiceName, - http.StatusNotFound, - }, - { - "No name provided", - createMockWithOutlines([]mockOutline{ - {"GetAddressableByName", []interface{}{""}, []interface{}{contract.Addressable{}, db.ErrNotFound}}, - }), - "", - http.StatusNotFound, - }, - { - "Invalid name", - nil, - "%ERR", - http.StatusBadRequest, - }, - { - "Addressable lookup error", - createMockWithOutlines([]mockOutline{ - {"GetAddressableByName", []interface{}{testDeviceServiceName}, []interface{}{contract.Addressable{}, testError}}, - }), - testDeviceServiceName, - http.StatusInternalServerError, - }, - { - "Device services lookup error", - createMockWithOutlines([]mockOutline{ - {"GetAddressableByName", []interface{}{testDeviceServiceName}, []interface{}{testAddressable, nil}}, - {"GetDeviceServicesByAddressableId", []interface{}{testDeviceServiceId}, []interface{}{nil, testError}}, - }), - testDeviceServiceName, - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetServiceByAddressableName( - rr, - createDeviceServiceRequest(http.MethodGet, ADDRESSABLENAME, tt.value), - tt.dbMock, - errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestGetServiceByAddressableId(t *testing.T) { - tests := []struct { - name string - dbMock interfaces.DBClient - value string - expectedStatus int - }{ - { - "OK", - createMockWithOutlines([]mockOutline{ - {"GetAddressableById", []interface{}{testDeviceServiceId}, []interface{}{testAddressable, nil}}, - {"GetDeviceServicesByAddressableId", []interface{}{testDeviceServiceId}, []interface{}{testDeviceServices, nil}}, - }), - testDeviceServiceId, - http.StatusOK, - }, - { - "No ID provided", - createMockWithOutlines([]mockOutline{ - {"GetAddressableByName", []interface{}{""}, []interface{}{contract.Addressable{}, db.ErrNotFound}}, - }), - "", - http.StatusNotFound, - }, - { - "Addressable not found", - createMockWithOutlines([]mockOutline{ - {"GetAddressableById", []interface{}{testDeviceServiceId}, []interface{}{contract.Addressable{}, db.ErrNotFound}}, - }), - testDeviceServiceId, - http.StatusNotFound, - }, - { - "Addressable lookup error", - createMockWithOutlines([]mockOutline{ - {"GetAddressableById", []interface{}{testDeviceServiceId}, []interface{}{contract.Addressable{}, testError}}, - }), - testDeviceServiceId, - http.StatusInternalServerError, - }, - { - "Device services lookup error", - createMockWithOutlines([]mockOutline{ - {"GetAddressableById", []interface{}{testDeviceServiceId}, []interface{}{testAddressable, nil}}, - {"GetDeviceServicesByAddressableId", []interface{}{testDeviceServiceId}, []interface{}{nil, testError}}, - }), - testDeviceServiceId, - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetServiceByAddressableId( - rr, - createDeviceServiceRequest(http.MethodGet, ADDRESSABLEID, tt.value), - tt.dbMock, - errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestUpdateOpStateById(t *testing.T) { - operatingStateEnabled := testDeviceService - operatingStateEnabled.OperatingState = testOperatingState - - tests := []struct { - name string - req *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - {"OK", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{ID: testDeviceServiceId, OPSTATE: ENABLED}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceById", []interface{}{testDeviceServiceId}, []interface{}{testDeviceService, nil}}, - {"UpdateDeviceService", []interface{}{operatingStateEnabled}, []interface{}{nil, nil}}, - }), - http.StatusOK, - }, - {"Invalid operating state", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{ID: testDeviceServiceId, OPSTATE: UNLOCKED}), - nil, - http.StatusBadRequest, - }, - {"Device service not found", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{ID: testDeviceServiceId, OPSTATE: ENABLED}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceById", []interface{}{testDeviceServiceId}, []interface{}{contract.DeviceService{}, db.ErrNotFound}}, - }), - http.StatusNotFound, - }, - {"Device service lookup error", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{ID: testDeviceServiceId, OPSTATE: ENABLED}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceById", []interface{}{testDeviceServiceId}, []interface{}{contract.DeviceService{}, testError}}, - }), - http.StatusInternalServerError, - }, - {"Update error", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{ID: testDeviceServiceId, OPSTATE: ENABLED}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceById", []interface{}{testDeviceServiceId}, []interface{}{testDeviceService, nil}}, - {"UpdateDeviceService", []interface{}{operatingStateEnabled}, []interface{}{testError, nil}}, - }), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restUpdateServiceOpStateById(rr, tt.req, tt.dbMock, errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestUpdateOpStateByName(t *testing.T) { - operatingStateEnabled := testDeviceService - operatingStateEnabled.OperatingState = testOperatingState - - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - {"OK", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{NAME: testDeviceServiceName, OPSTATE: ENABLED}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceByName", []interface{}{testDeviceServiceName}, []interface{}{testDeviceService, nil}}, - {"UpdateDeviceService", []interface{}{operatingStateEnabled}, []interface{}{nil, nil}}, - }), - http.StatusOK, - }, - {"Invalid operating state", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{NAME: testDeviceServiceName, OPSTATE: UNLOCKED}), - nil, - http.StatusBadRequest, - }, - {"Invalid name", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{NAME: "%ERR", OPSTATE: ENABLED}), - nil, - http.StatusBadRequest, - }, - {"Device service not found", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{NAME: testDeviceServiceName, OPSTATE: ENABLED}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceByName", []interface{}{testDeviceServiceName}, []interface{}{contract.DeviceService{}, db.ErrNotFound}}, - }), - http.StatusNotFound, - }, - {"Device service lookup error", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{NAME: testDeviceServiceName, OPSTATE: ENABLED}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceByName", []interface{}{testDeviceServiceName}, []interface{}{contract.DeviceService{}, testError}}, - }), - http.StatusInternalServerError, - }, - {"Update error", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{NAME: testDeviceServiceName, OPSTATE: ENABLED}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceByName", []interface{}{testDeviceServiceName}, []interface{}{testDeviceService, nil}}, - {"UpdateDeviceService", []interface{}{operatingStateEnabled}, []interface{}{testError, nil}}, - }), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restUpdateServiceOpStateByName( - rr, - tt.request, - tt.dbMock, - errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestUpdateAdminStateById(t *testing.T) { - adminStateEnabled := testDeviceService - adminStateEnabled.AdminState = testAdminState - lc := logger.MockLogger{} - - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - {"OK", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{ID: testDeviceServiceId, ADMINSTATE: UNLOCKED}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceById", []interface{}{testDeviceServiceId}, []interface{}{testDeviceService, nil}}, - {"UpdateDeviceService", []interface{}{adminStateEnabled}, []interface{}{nil, nil}}, - }), - http.StatusOK, - }, - {"Invalid admin state", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{ID: testDeviceServiceId, ADMINSTATE: ENABLED}), - nil, - http.StatusBadRequest, - }, - {"Device service not found", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{ID: testDeviceServiceId, ADMINSTATE: UNLOCKED}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceById", []interface{}{testDeviceServiceId}, []interface{}{contract.DeviceService{}, db.ErrNotFound}}, - }), - http.StatusNotFound, - }, - {"Device service lookup error", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{ID: testDeviceServiceId, ADMINSTATE: UNLOCKED}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceById", []interface{}{testDeviceServiceId}, []interface{}{contract.DeviceService{}, testError}}, - }), - http.StatusInternalServerError, - }, - {"Update error", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{ID: testDeviceServiceId, ADMINSTATE: UNLOCKED}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceById", []interface{}{testDeviceServiceId}, []interface{}{testDeviceService, nil}}, - {"UpdateDeviceService", []interface{}{adminStateEnabled}, []interface{}{testError, nil}}, - }), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restUpdateServiceAdminStateById( - rr, - tt.request, - lc, - tt.dbMock, - errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestUpdateAdminStateByName(t *testing.T) { - adminStateEnabled := testDeviceService - adminStateEnabled.AdminState = testAdminState - lc := logger.MockLogger{} - - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - {"OK", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{NAME: testDeviceServiceName, ADMINSTATE: UNLOCKED}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceByName", []interface{}{testDeviceServiceName}, []interface{}{testDeviceService, nil}}, - {"UpdateDeviceService", []interface{}{adminStateEnabled}, []interface{}{nil, nil}}, - }), - http.StatusOK, - }, - {"Invalid admin state", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{NAME: testDeviceServiceName, ADMINSTATE: ENABLED}), - nil, - http.StatusBadRequest, - }, - {"Invalid name", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{NAME: "%ERR", ADMINSTATE: UNLOCKED}), - nil, - http.StatusBadRequest, - }, - {"Device service not found", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{NAME: testDeviceServiceName, ADMINSTATE: UNLOCKED}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceByName", []interface{}{testDeviceServiceName}, []interface{}{contract.DeviceService{}, db.ErrNotFound}}, - }), - http.StatusNotFound, - }, - {"Device service lookup error", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{NAME: testDeviceServiceName, ADMINSTATE: UNLOCKED}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceByName", []interface{}{testDeviceServiceName}, []interface{}{contract.DeviceService{}, testError}}, - }), - http.StatusInternalServerError, - }, - {"Update error", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceService, map[string]string{NAME: testDeviceServiceName, ADMINSTATE: UNLOCKED}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceByName", []interface{}{testDeviceServiceName}, []interface{}{testDeviceService, nil}}, - {"UpdateDeviceService", []interface{}{adminStateEnabled}, []interface{}{testError, nil}}, - }), - http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restUpdateServiceAdminStateByName( - rr, - tt.request, - lc, - tt.dbMock, - errorconcept.NewErrorHandler(logger.NewMockClient())) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestUpdateServiceLastReportedById(t *testing.T) { - - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "ok", - createDeviceServiceRequestWithBody( - http.MethodPut, testDeviceServiceLastUpdated, map[string]string{ID: testDeviceServiceLastUpdatedId, LASTREPORTED: strconv.Itoa(lastReported)}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceById", []interface{}{testDeviceServiceLastUpdatedId}, []interface{}{testDeviceServiceLastUpdated, nil}}, - {"UpdateDeviceService", []interface{}{testDeviceServiceLastUpdated}, []interface{}{nil, nil}}, - }), - http.StatusOK, - }, - { - "request validation error", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceServiceLastUpdated, map[string]string{ID: testDeviceServiceLastUpdatedId, LASTREPORTED: ""}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceById", []interface{}{testDeviceServiceLastUpdatedId}, []interface{}{testDeviceServiceLastUpdated, testError}}, - {"UpdateDeviceService", []interface{}{testDeviceServiceLastUpdated}, []interface{}{nil, testError}}, - }), - http.StatusBadRequest, - }, - { - "device service lookup error", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceServiceLastUpdatedNotExist, map[string]string{ID: testDeviceServiceLastUpdatedNotExistId, LASTREPORTED: strconv.Itoa(lastReported)}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceById", []interface{}{testDeviceServiceLastUpdatedNotExistId}, []interface{}{contract.DeviceService{}, db.ErrNotFound}}, - {"UpdateDeviceService", []interface{}{testDeviceServiceLastUpdatedNotExist}, []interface{}{contract.DeviceService{}, db.ErrNotFound}}, - }), - http.StatusNotFound, - }, - { - "update service last reported error", - createDeviceServiceRequestWithBody(http.MethodPut, testDeviceServiceLastUpdated, map[string]string{ID: testDeviceServiceLastUpdatedId, LASTREPORTED: strconv.Itoa(lastReported)}), - createMockWithOutlines([]mockOutline{ - {"GetDeviceServiceById", []interface{}{testDeviceServiceLastUpdatedId}, []interface{}{testDeviceServiceLastUpdated, nil}}, - {"UpdateDeviceService", []interface{}{testDeviceServiceLastUpdated}, []interface{}{testError}}, - }), - http.StatusServiceUnavailable, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - loggerMock := logger.NewMockClient() - restUpdateServiceLastReportedById( - rr, - tt.request, - loggerMock, - tt.dbMock, - errorconcept.NewErrorHandler(loggerMock)) - response := rr.Result() - - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func createGetDeviceServiceLoaderMock(howMany int) interfaces.DBClient { - services := []contract.DeviceService{} - for i := 0; i < howMany; i++ { - services = append(services, contract.DeviceService{Name: "Test Device Service"}) - } - - dbMock := &mocks.DBClient{} - dbMock.On("GetAllDeviceServices").Return(services, nil) - return dbMock -} - -func createGetDeviceServiceLoaderMockFail() interfaces.DBClient { - dbMock := &mocks.DBClient{} - dbMock.On("GetAllDeviceServices").Return(nil, errors.New("unexpected error")) - return dbMock -} - -func createDeviceServiceRequest(httpMethod string, pathParamName string, pathParamValue string) *http.Request { - req := httptest.NewRequest(httpMethod, testDeviceServiceURI, nil) - return mux.SetURLVars(req, map[string]string{pathParamName: pathParamValue}) -} - -func createDeviceServiceRequestWithBody( - httpMethod string, - deviceService contract.DeviceService, - pathParams map[string]string) *http.Request { - - // if your JSON marshalling fails you've got bigger problems - body, _ := json.Marshal(deviceService) - - req := httptest.NewRequest(httpMethod, testDeviceServiceURI, bytes.NewReader(body)) - - return mux.SetURLVars(req, pathParams) -} diff --git a/internal/core/metadata/rest_provisionwatcher.go b/internal/core/metadata/rest_provisionwatcher.go deleted file mode 100644 index fef642ea14..0000000000 --- a/internal/core/metadata/rest_provisionwatcher.go +++ /dev/null @@ -1,627 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * - * 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 metadata - -import ( - "encoding/json" - "errors" - "net/http" - "net/url" - "reflect" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/config" - pwErrors "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" -) - -// creation endpoints -func restAddProvisionWatcher( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - defer r.Body.Close() - var pw models.ProvisionWatcher - var err error - - if err = json.NewDecoder(r.Body).Decode(&pw); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.JsonDecoding) - return - } - - // Check if the name exists - if pw.Name == "" { - errorHandler.Handle( - w, - errors.New("no name provided for new provision watcher"), - errorconcept.Common.InvalidRequest_StatusBadRequest, - ) - return - } - - // Check if the device profile exists - var profile models.DeviceProfile - if pw.Profile.Id != "" { - profile, err = dbClient.GetDeviceProfileById(pw.Profile.Id) - // we don't want to fail if we haven't found anything at this point because we've yet to do name lookup - if err != nil && err != db.ErrNotFound { - errorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - } - // we only want to do a lookup by name if ID does not exist or if ID does not exist in the DB - if pw.Profile.Id == "" || err == db.ErrNotFound { - if profile, err = dbClient.GetDeviceProfileByName(pw.Profile.Name); err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.ProvisionWatcher.DeviceProfileNotFound_StatusConflict, - errorconcept.Default.InternalServerError, - ) - return - } - } - pw.Profile = profile - - // Check if the device service exists - var service models.DeviceService - if pw.Service.Id != "" { - service, err = dbClient.GetDeviceServiceById(pw.Service.Id) - // we don't want to fail if we haven't found anything at this point because we've yet to do name lookup - if err != nil && err != db.ErrNotFound { - errorHandler.Handle(w, err, errorconcept.Default.ServiceUnavailable) - return - } - } - - // we only want to do a lookup by name if ID does not exist or if ID does not exist in the DB - if pw.Service.Id == "" || err == db.ErrNotFound { - if service, err = dbClient.GetDeviceServiceByName(pw.Service.Name); err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.ProvisionWatcher.DeviceServiceNotFound_StatusConflict, - errorconcept.Default.ServiceUnavailable, - ) - return - } - } - pw.Service = service - - id, err := dbClient.AddProvisionWatcher(pw) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.ProvisionWatcher.NotUnique, - errorconcept.Default.ServiceUnavailable, - ) - return - } - pw.Id = id - - // Notify Associates - if err = notifyProvisionWatcherAssociates(pw, http.MethodPost, lc, dbClient); err != nil { - errorHandler.Handle(w, err, errorconcept.Default.ServiceUnavailable) - return - } - - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(id)) -} - -// read endpoints -func restGetProvisionWatchers( - w http.ResponseWriter, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler, - configuration *config.ConfigurationStruct) { - - res, err := dbClient.GetAllProvisionWatchers() - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.RetrieveError_StatusInternalServer) - return - } - - // Check the length - if err = checkMaxLimit(len(res), configuration); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.LimitExceeded) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(&res) -} - -func restGetProvisionWatcherById( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var id = vars[ID] - - res, err := dbClient.GetProvisionWatcherById(id) - if err != nil { - errorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.ProvisionWatcher.NotFoundById, - errorconcept.ProvisionWatcher.InvalidID, - }, - errorconcept.Default.InternalServerError, - ) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -func restGetProvisionWatcherByName( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Default.BadRequest) - return - } - - res, err := dbClient.GetProvisionWatcherByName(n) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.ProvisionWatcher.NotFoundByName, - errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -func restGetProvisionWatchersByProfileId( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var pid = vars[ID] - - // Check if the device profile exists - if _, err := dbClient.GetDeviceProfileById(pid); err != nil { - errorHandler.Handle(w, err, errorconcept.ProvisionWatcher.DeviceProfileNotFound_StatusNotFound) - return - } - - res, err := dbClient.GetProvisionWatchersByProfileId(pid) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.ProvisionWatcher.NotFoundById, - errorconcept.Common.RetrieveError_StatusInternalServer, - ) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -func restGetProvisionWatchersByProfileName( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - pn, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the device profile exists - dp, err := dbClient.GetDeviceProfileByName(pn) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.ProvisionWatcher.DeviceProfileNotFound_StatusNotFound, - errorconcept.Default.InternalServerError, - ) - return - } - - res, err := dbClient.GetProvisionWatchersByProfileId(dp.Id) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.ProvisionWatcher.NotFoundById, - errorconcept.Common.RetrieveError_StatusInternalServer, - ) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -func restGetProvisionWatchersByServiceId( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var sid = vars[ID] - - // Check if the device service exists - if _, err := dbClient.GetDeviceServiceById(sid); err != nil { - errorHandler.Handle(w, err, errorconcept.ProvisionWatcher.DeviceServiceNotFound_StatusNotFound) - return - } - - res, err := dbClient.GetProvisionWatchersByServiceId(sid) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.ProvisionWatcher.NotFoundById, - errorconcept.Common.RetrieveError_StatusInternalServer, - ) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -func restGetProvisionWatchersByServiceName( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - sn, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the device service exists - ds, err := dbClient.GetDeviceServiceByName(sn) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.ProvisionWatcher.DeviceServiceNotFound_StatusNotFound, - errorconcept.Default.InternalServerError, - ) - return - } - - // Get the provision watchers - res, err := dbClient.GetProvisionWatchersByServiceId(ds.Id) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.ProvisionWatcher.NotFoundById, - errorconcept.Common.RetrieveError_StatusInternalServer, - ) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -func restGetProvisionWatchersByIdentifier( - w http.ResponseWriter, - r *http.Request, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - k, err := url.QueryUnescape(vars[KEY]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - v, err := url.QueryUnescape(vars[VALUE]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - res, err := dbClient.GetProvisionWatchersByIdentifier(k, v) - if err != nil { - errorHandler.HandleOneVariant( - w, - err, - errorconcept.ProvisionWatcher.RetrieveError_StatusNotFound, - errorconcept.Common.RetrieveError_StatusInternalServer, - ) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - _ = json.NewEncoder(w).Encode(res) -} - -// update endpoints -func restUpdateProvisionWatcher( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - defer r.Body.Close() - var to models.ProvisionWatcher - if err := json.NewDecoder(r.Body).Decode(&to); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusServiceUnavailable) - return - } - - from, err := getProvisionWatcher(to, dbClient) - if err != nil { - errorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.ProvisionWatcher.NotFoundByName, - errorconcept.ProvisionWatcher.InvalidID, - errorconcept.ProvisionWatcher.NameCollision, - }, - errorconcept.Common.RetrieveError_StatusInternalServer, - ) - return - } - - // from is the object currently in the database; "the provision watcher we are updating *from*" - // to is the target object state; "the provision watcher we are updating *to*" - if to.Origin != 0 { - from.Origin = to.Origin - } - - if to.Name != "" { - from.Name = to.Name - } - - if to.Identifiers != nil { - from.Identifiers = to.Identifiers - } - - if to.BlockingIdentifiers != nil { - from.BlockingIdentifiers = to.BlockingIdentifiers - } - - if !reflect.DeepEqual(to.Profile, models.DeviceProfile{}) { - from.Profile = to.Profile - } - - if !reflect.DeepEqual(to.Service, models.DeviceService{}) { - from.Service = to.Service - } - - // always update admin state - from.AdminState = to.AdminState - - if err := dbClient.UpdateProvisionWatcher(from); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.UpdateError_StatusInternalServer) - return - } - - if err := notifyProvisionWatcherAssociates(from, http.MethodPut, lc, dbClient); err != nil { - errorHandler.Handle(w, err, errorconcept.Default.InternalServerError) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("true")) -} - -// getProvisionWatcher is a helper function that first attempts to lookup by ID, and, failing that, -// will attempt a lookup by name before finally bubbling the error up -func getProvisionWatcher( - lookup models.ProvisionWatcher, - dbClient interfaces.DBClient) (models.ProvisionWatcher, error) { - - // if ID is the empty string, do not attempt a lookup by ID. doing so can foul up some ID validators, like BSON - var byID models.ProvisionWatcher - if lookup.Id != "" { - var err error - byID, err = dbClient.GetProvisionWatcherById(lookup.Id) - if err != nil && err != db.ErrNotFound { // ignore ErrNotFound, we can still do a name lookup - return models.ProvisionWatcher{}, err - } - } - - byName, err := dbClient.GetProvisionWatcherByName(lookup.Name) - if err != nil { - return models.ProvisionWatcher{}, err - } - - // ensure that neither the lookup by name nor the lookup by ID have unwanted name collisions - if namesCollide(byName, lookup) { - return models.ProvisionWatcher{}, pwErrors.NewErrNameCollision(byName.Name, byName.Id, lookup.Id) - } else if namesCollide(byName, byID) { - return models.ProvisionWatcher{}, pwErrors.NewErrNameCollision(byName.Name, byName.Id, byID.Id) - } - - // only use the result of the name lookup if we didn't get anything from the ID lookup - if reflect.DeepEqual(byID, models.ProvisionWatcher{}) { - return byName, nil - } - - return byID, nil -} - -// namesCollide consolidates the logic of determining whether provision watcher data that we are attempting to update -// will have either an ID or a name collision -func namesCollide(base models.ProvisionWatcher, update models.ProvisionWatcher) bool { - // breaking this down piece by piece: - // there is a collision if base and update have the same name ... - // - // ... only if the IDs don't match. If the IDs match, then we are correctly performing an update on an existing PW - // - // if the update PW has no ID, then we never say there is a collision and always assume that this is a valid update. - // or, thinking about it differently, this clause is "update by name" - return base.Name == update.Name && base.Id != update.Id && update.Id != "" -} - -func restDeleteProvisionWatcherById( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - var id = vars[ID] - - // Check if the provision watcher exists - pw, err := dbClient.GetProvisionWatcherById(id) - if err != nil { - errorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.ProvisionWatcher.NotFoundById, - errorconcept.ProvisionWatcher.InvalidID, - }, - errorconcept.Default.InternalServerError) - return - } - - if err = deleteProvisionWatcher(pw, w, lc, dbClient, errorHandler); err != nil { - errorHandler.Handle(w, err, errorconcept.ProvisionWatcher.DeleteError_StatusInternalServer) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("true")) -} - -func restDeleteProvisionWatcherByName( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) { - - vars := mux.Vars(r) - n, err := url.QueryUnescape(vars[NAME]) - if err != nil { - errorHandler.Handle(w, err, errorconcept.Common.InvalidRequest_StatusBadRequest) - return - } - - // Check if the provision watcher exists - pw, err := dbClient.GetProvisionWatcherByName(n) - if err != nil { - errorHandler.HandleManyVariants( - w, - err, - []errorconcept.ErrorConceptType{ - errorconcept.ProvisionWatcher.NotFoundByName, - errorconcept.ProvisionWatcher.InvalidID, - }, - errorconcept.Default.InternalServerError) - return - } - - if err = deleteProvisionWatcher(pw, w, lc, dbClient, errorHandler); err != nil { - errorHandler.Handle(w, err, errorconcept.ProvisionWatcher.DeleteError_StatusInternalServer) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("true")) -} - -// delete endpoints -func deleteProvisionWatcher( - pw models.ProvisionWatcher, - w http.ResponseWriter, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - errorHandler errorconcept.ErrorHandler) error { - - if err := dbClient.DeleteProvisionWatcherById(pw.Id); err != nil { - errorHandler.Handle(w, err, errorconcept.Common.DeleteError) - return err - } - - err := notifyProvisionWatcherAssociates(pw, http.MethodDelete, lc, dbClient) - if err != nil { - lc.Error("Problem notifying associated device services to provision watcher: " + err.Error()) - return err - } - - return nil -} - -// notifyProvisionWatcherAssociates triggers the callbacks in the device service attached to this provision watcher. -func notifyProvisionWatcherAssociates( - pw models.ProvisionWatcher, - action string, - lc logger.LoggingClient, - dbClient interfaces.DBClient) error { - - // Get the device service for the provision watcher - ds, err := dbClient.GetDeviceServiceById(pw.Service.Id) - if err != nil { - // try by name - ds, err = dbClient.GetDeviceServiceByName(pw.Service.Name) - if err != nil { - return err - } - } - - // notify the device service - err = notifyAssociates([]models.DeviceService{ds}, pw.Id, action, models.PROVISIONWATCHER, lc) - if err != nil { - return err - } - - return nil -} diff --git a/internal/core/metadata/router.go b/internal/core/metadata/router.go deleted file mode 100644 index 3b63f859f1..0000000000 --- a/internal/core/metadata/router.go +++ /dev/null @@ -1,1091 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * - * 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 metadata - -import ( - "net/http" - - metadataContainer "github.com/edgexfoundry/edgex-go/internal/core/metadata/container" - "github.com/edgexfoundry/edgex-go/internal/pkg" - "github.com/edgexfoundry/edgex-go/internal/pkg/bootstrap/container" - errorContainer "github.com/edgexfoundry/edgex-go/internal/pkg/container" - "github.com/edgexfoundry/edgex-go/internal/pkg/correlation" - "github.com/edgexfoundry/edgex-go/internal/pkg/telemetry" - - bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/container" - "github.com/edgexfoundry/go-mod-bootstrap/v2/di" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - - "github.com/gorilla/mux" -) - -func loadRestRoutes(r *mux.Router, dic *di.Container) { - // Ping Resource - r.HandleFunc( - clients.ApiPingRoute, - func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set(clients.ContentType, clients.ContentTypeText) - _, _ = w.Write([]byte("pong")) - }).Methods(http.MethodGet) - - // Configuration - r.HandleFunc( - clients.ApiConfigRoute, - func(w http.ResponseWriter, _ *http.Request) { - pkg.Encode(metadataContainer.ConfigurationFrom(dic.Get), w, bootstrapContainer.LoggingClientFrom(dic.Get)) - }).Methods(http.MethodGet) - - // Metrics - r.HandleFunc( - clients.ApiMetricsRoute, - func(w http.ResponseWriter, _ *http.Request) { - pkg.Encode(telemetry.NewSystemUsage(), w, bootstrapContainer.LoggingClientFrom(dic.Get)) - }).Methods(http.MethodGet) - - // Version - r.HandleFunc(clients.ApiVersionRoute, pkg.VersionHandler).Methods(http.MethodGet) - - b := r.PathPrefix(clients.ApiBase).Subrouter() - - loadDeviceRoutes(b, dic) - loadDeviceProfileRoutes(b, dic) - loadDeviceReportRoutes(b, dic) - loadDeviceServiceRoutes(b, dic) - loadProvisionWatcherRoutes(b, dic) - loadAddressableRoutes(b, dic) - loadCommandRoutes(b, dic) - - r.Use(correlation.ManageHeader) - r.Use(correlation.LoggingMiddleware(bootstrapContainer.LoggingClientFrom(dic.Get))) -} - -func loadDeviceRoutes(b *mux.Router, dic *di.Container) { - // /api/v1/" + DEVICE - b.HandleFunc( - "/"+DEVICE, - func(w http.ResponseWriter, r *http.Request) { - restAddNewDevice( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.NotificationsClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPost) - b.HandleFunc( - "/"+DEVICE, - func(w http.ResponseWriter, r *http.Request) { - restUpdateDevice(w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.NotificationsClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPut) - b.HandleFunc( - "/"+DEVICE, - func(w http.ResponseWriter, r *http.Request) { - restGetAllDevices( - w, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - - d := b.PathPrefix("/" + DEVICE).Subrouter() - - d.HandleFunc( - "/"+LABEL+"/{"+LABEL+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetDevicesWithLabel( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - d.HandleFunc( - "/"+PROFILE+"/{"+PROFILEID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetDeviceByProfileId( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - d.HandleFunc( - "/"+SERVICE+"/{"+SERVICEID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetDeviceByServiceId( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - d.HandleFunc( - "/"+SERVICENAME+"/{"+SERVICENAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetDeviceByServiceName( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - d.HandleFunc( - "/"+PROFILENAME+"/{"+PROFILENAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetDeviceByProfileName( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - // /api/v1/" + DEVICE" + ID + " - d.HandleFunc( - "/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetDeviceById(w, r, container.DBClientFrom(dic.Get), errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - d.HandleFunc( - "/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restSetDeviceStateById( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.NotificationsClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPut) - d.HandleFunc( - "/"+ID+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteDeviceById( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.NotificationsClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodDelete) - d.HandleFunc( - "/{"+ID+"}/"+URLLASTREPORTED+"/{"+LASTREPORTED+"}", - func(w http.ResponseWriter, r *http.Request) { - restSetDeviceLastReportedById( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.NotificationsClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPut) - d.HandleFunc( - "/{"+ID+"}/"+URLLASTREPORTED+"/{"+LASTREPORTED+"}/{"+LASTREPORTEDNOTIFY+"}", - func(w http.ResponseWriter, r *http.Request) { - restSetDeviceLastReportedByIdNotify( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.NotificationsClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPut) - d.HandleFunc( - "/{"+ID+"}/"+URLLASTCONNECTED+"/{"+LASTCONNECTED+"}", - func(w http.ResponseWriter, r *http.Request) { - restSetDeviceLastConnectedById( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.NotificationsClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPut) - d.HandleFunc( - "/{"+ID+"}/"+URLLASTCONNECTED+"/{"+LASTCONNECTED+"}/{"+LASTCONNECTEDNOTIFY+"}", - func(w http.ResponseWriter, r *http.Request) { - restSetLastConnectedByIdNotify( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.NotificationsClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPut) - d.HandleFunc( - "/"+CHECK+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restCheckForDevice( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - // /api/v1/" + DEVICE/" + NAME + " - n := d.PathPrefix("/" + NAME).Subrouter() - n.HandleFunc( - "/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetDeviceByName( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - n.HandleFunc( - "/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteDeviceByName( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.NotificationsClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodDelete) - n.HandleFunc( - "/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restSetDeviceStateByDeviceName( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.NotificationsClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPut) - n.HandleFunc( - "/{"+NAME+"}/"+URLLASTREPORTED+"/{"+LASTREPORTED+"}", - func(w http.ResponseWriter, - r *http.Request) { - restSetDeviceLastReportedByName( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.NotificationsClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPut) - n.HandleFunc( - "/{"+NAME+"}/"+URLLASTREPORTED+"/{"+LASTREPORTED+"}/{"+LASTREPORTEDNOTIFY+"}", - func(w http.ResponseWriter, r *http.Request) { - restSetDeviceLastReportedByNameNotify( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.NotificationsClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPut) - n.HandleFunc( - "/{"+NAME+"}/"+URLLASTCONNECTED+"/{"+LASTCONNECTED+"}", - func(w http.ResponseWriter, - r *http.Request) { - restSetDeviceLastConnectedByName( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.NotificationsClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPut) - n.HandleFunc( - "/{"+NAME+"}/"+URLLASTCONNECTED+"/{"+LASTCONNECTED+"}/{"+LASTCONNECTEDNOTIFY+"}", - func(w http.ResponseWriter, r *http.Request) { - restSetDeviceLastConnectedByNameNotify( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.NotificationsClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPut) - -} - -func loadDeviceProfileRoutes(b *mux.Router, dic *di.Container) { - ///api/v1/" + DEVICEPROFILE + " - b.HandleFunc( - "/"+DEVICEPROFILE+"", - func(w http.ResponseWriter, r *http.Request) { - restGetAllDeviceProfiles( - w, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+DEVICEPROFILE+"", - func(w http.ResponseWriter, r *http.Request) { - restAddDeviceProfile( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.CoreDataValueDescriptorClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPost) - b.HandleFunc( - "/"+DEVICEPROFILE+"", - func(w http.ResponseWriter, r *http.Request) { - restUpdateDeviceProfile( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.CoreDataValueDescriptorClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPut) - - dp := b.PathPrefix("/" + DEVICEPROFILE).Subrouter() - dp.HandleFunc( - "/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetProfileByProfileId( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - dp.HandleFunc( - "/"+ID+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteProfileByProfileId( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodDelete) - dp.HandleFunc( - "/"+UPLOADFILE, - func(w http.ResponseWriter, r *http.Request) { - restAddProfileByYaml( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.CoreDataValueDescriptorClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPost) - dp.HandleFunc( - "/"+UPLOAD, - func(w http.ResponseWriter, r *http.Request) { - restAddProfileByYamlRaw( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.CoreDataValueDescriptorClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPost) - dp.HandleFunc( - "/"+MODEL+"/{"+MODEL+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetProfileByModel( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - dp.HandleFunc( - "/"+LABEL+"/{"+LABEL+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetProfileWithLabel( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - // /api/v1/" + DEVICEPROFILE + "/" + MANUFACTURER + " - dpm := dp.PathPrefix("/" + MANUFACTURER).Subrouter() - dpm.HandleFunc( - "/{"+MANUFACTURER+"}/"+MODEL+"/{"+MODEL+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetProfileByManufacturerModel( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - dpm.HandleFunc( - "/{"+MANUFACTURER+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetProfileByManufacturer( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - // /api/v1/" + DEVICEPROFILE + "/" + NAME + " - dpn := dp.PathPrefix("/" + NAME).Subrouter() - dpn.HandleFunc( - "/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetProfileByName( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - dpn.HandleFunc( - "/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteProfileByName( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodDelete) - - // /api/v1/" + DEVICEPROFILE + "/" + YAML - dpy := dp.PathPrefix("/" + YAML).Subrouter() - // TODO add functionality - dpy.HandleFunc( - "/"+NAME+"/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetYamlProfileByName( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - dpy.HandleFunc( - "/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetYamlProfileById( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - -} -func loadDeviceReportRoutes(b *mux.Router, dic *di.Container) { - // /api/v1/devicereport - b.HandleFunc( - "/"+DEVICEREPORT, - func(w http.ResponseWriter, r *http.Request) { - restGetAllDeviceReports( - w, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - - b.HandleFunc( - "/"+DEVICEREPORT, - func(w http.ResponseWriter, r *http.Request) { - restAddDeviceReport( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodPost) - b.HandleFunc( - "/"+DEVICEREPORT, - func(w http.ResponseWriter, r *http.Request) { - restUpdateDeviceReport( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodPut) - - dr := b.PathPrefix("/" + DEVICEREPORT).Subrouter() - dr.HandleFunc( - "/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetReportById(w, r, container.DBClientFrom(dic.Get), errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - dr.HandleFunc( - "/"+ID+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteReportById( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodDelete) - dr.HandleFunc( - "/"+DEVICENAME+"/{"+DEVICENAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetDeviceReportByDeviceName( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - // /api/v1/devicereport/" + NAME + " - drn := dr.PathPrefix("/" + NAME).Subrouter() - drn.HandleFunc( - "/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetReportByName( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - drn.HandleFunc( - "/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteReportByName( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodDelete) - - // /api/v1/devicereport/valueDescriptorsFor/devicename - drvd := dr.PathPrefix("/" + VALUEDESCRIPTORSFOR).Subrouter() - drvd.HandleFunc( - "/{"+DEVICENAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetValueDescriptorsForDeviceName( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) -} -func loadDeviceServiceRoutes(b *mux.Router, dic *di.Container) { - // /api/v1/deviceservice - b.HandleFunc( - "/"+DEVICESERVICE, - func(w http.ResponseWriter, r *http.Request) { - restGetAllDeviceServices( - w, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+DEVICESERVICE, - func(w http.ResponseWriter, r *http.Request) { - restAddDeviceService( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodPost) - - b.HandleFunc( - "/"+DEVICESERVICE, - func(w http.ResponseWriter, r *http.Request) { - restUpdateDeviceService( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodPut) - - ds := b.PathPrefix("/" + DEVICESERVICE).Subrouter() - ds.HandleFunc( - "/"+ADDRESSABLENAME+"/{"+ADDRESSABLENAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetServiceByAddressableName( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - ds.HandleFunc( - "/"+ADDRESSABLE+"/{"+ADDRESSABLEID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetServiceByAddressableId( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - ds.HandleFunc( - "/"+LABEL+"/{"+LABEL+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetServiceWithLabel( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - // /api/v1/deviceservice/" + NAME + " - dsn := ds.PathPrefix("/" + NAME).Subrouter() - dsn.HandleFunc( - "/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetServiceByName( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - dsn.HandleFunc( - "/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteServiceByName( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.NotificationsClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodDelete) - dsn.HandleFunc( - "/{"+NAME+"}/"+OPSTATE+"/{"+OPSTATE+"}", - func(w http.ResponseWriter, r *http.Request) { - restUpdateServiceOpStateByName( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodPut) - dsn.HandleFunc( - "/{"+NAME+"}/"+URLADMINSTATE+"/{"+ADMINSTATE+"}", - func(w http.ResponseWriter, - r *http.Request) { - restUpdateServiceAdminStateByName( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodPut) - - dsn.HandleFunc( - "/{"+NAME+"}/"+URLLASTREPORTED+"/{"+LASTREPORTED+"}", - func(w http.ResponseWriter, - r *http.Request) { - restUpdateServiceLastReportedByName( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodPut) - dsn.HandleFunc( - "/{"+NAME+"}/"+URLLASTCONNECTED+"/{"+LASTCONNECTED+"}", - func(w http.ResponseWriter, r *http.Request) { - restUpdateServiceLastConnectedByName( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodPut) - - // /api/v1/" + DEVICESERVICE + ID + " - ds.HandleFunc( - "/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetServiceById(w, r, container.DBClientFrom(dic.Get), errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - ds.HandleFunc( - "/"+ID+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteServiceById( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.NotificationsClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodDelete) - ds.HandleFunc( - "/"+ID+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteServiceById( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.NotificationsClientFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodDelete) - - ds.HandleFunc( - "/{"+ID+"}/"+OPSTATE+"/{"+OPSTATE+"}", - func(w http.ResponseWriter, r *http.Request) { - restUpdateServiceOpStateById( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodPut) - - ds.HandleFunc( - "/{"+ID+"}/"+URLADMINSTATE+"/{"+ADMINSTATE+"}", - func(w http.ResponseWriter, r *http.Request) { - restUpdateServiceAdminStateById( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodPut) - - ds.HandleFunc( - "/{"+ID+"}/"+URLLASTREPORTED+"/{"+LASTREPORTED+"}", - func(w http.ResponseWriter, - r *http.Request) { - restUpdateServiceLastReportedById( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodPut) - ds.HandleFunc( - "/{"+ID+"}/"+URLLASTCONNECTED+"/{"+LASTCONNECTED+"}", - func(w http.ResponseWriter, - r *http.Request) { - restUpdateServiceLastConnectedById( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodPut) -} - -func loadProvisionWatcherRoutes(b *mux.Router, dic *di.Container) { - b.HandleFunc( - "/"+PROVISIONWATCHER, - func(w http.ResponseWriter, r *http.Request) { - restAddProvisionWatcher( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodPost) - b.HandleFunc( - "/"+PROVISIONWATCHER, - func(w http.ResponseWriter, r *http.Request) { - restUpdateProvisionWatcher( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodPut) - b.HandleFunc( - "/"+PROVISIONWATCHER, - func(w http.ResponseWriter, r *http.Request) { - restGetProvisionWatchers( - w, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - - pw := b.PathPrefix("/" + PROVISIONWATCHER).Subrouter() - // /api/v1/provisionwatcher - pw.HandleFunc( - "/"+ID+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteProvisionWatcherById( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodDelete) - - pw.HandleFunc( - "/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetProvisionWatcherById( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - pw.HandleFunc( - "/"+NAME+"/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteProvisionWatcherByName( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodDelete) - - pw.HandleFunc( - "/"+NAME+"/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetProvisionWatcherByName( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - pw.HandleFunc( - "/"+PROFILENAME+"/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetProvisionWatchersByProfileName( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - pw.HandleFunc( - "/"+PROFILE+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetProvisionWatchersByProfileId( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - pw.HandleFunc( - "/"+SERVICE+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetProvisionWatchersByServiceId( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - pw.HandleFunc( - "/"+SERVICENAME+"/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetProvisionWatchersByServiceName( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - pw.HandleFunc( - "/"+IDENTIFIER+"/{"+KEY+"}/{"+VALUE+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetProvisionWatchersByIdentifier( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) -} - -func loadAddressableRoutes(b *mux.Router, dic *di.Container) { - // /api/v1/" + ADDRESSABLE + " - b.HandleFunc( - "/"+ADDRESSABLE, - func(w http.ResponseWriter, r *http.Request) { - restGetAllAddressables( - w, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - - b.HandleFunc( - "/"+ADDRESSABLE, - func(w http.ResponseWriter, r *http.Request) { - restAddAddressable( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodPost) - - b.HandleFunc( - "/"+ADDRESSABLE, - func(w http.ResponseWriter, r *http.Request) { - restUpdateAddressable( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodPut) - - a := b.PathPrefix("/" + ADDRESSABLE).Subrouter() - a.HandleFunc( - "/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetAddressableById( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - a.HandleFunc( - "/"+ID+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteAddressableById( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodDelete) - a.HandleFunc( - "/"+NAME+"/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetAddressableByName( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - a.HandleFunc( - "/"+NAME+"/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteAddressableByName( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodDelete) - a.HandleFunc( - "/"+TOPIC+"/{"+TOPIC+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetAddressableByTopic( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - a.HandleFunc( - "/"+PORT+"/{"+PORT+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetAddressableByPort( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - a.HandleFunc( - "/"+PUBLISHER+"/{"+PUBLISHER+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetAddressableByPublisher( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - a.HandleFunc( - "/"+ADDRESS+"/{"+ADDRESS+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetAddressableByAddress( - w, - r, - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) -} - -func loadCommandRoutes(b *mux.Router, dic *di.Container) { - // /api/v1/command - b.HandleFunc( - "/"+COMMAND, - func(w http.ResponseWriter, r *http.Request) { - restGetAllCommands( - w, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - - b.HandleFunc( - "/"+COMMAND, - func(w http.ResponseWriter, r *http.Request) { - restGetAllCommands( - w, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get), - metadataContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - - c := b.PathPrefix("/" + COMMAND).Subrouter() - c.HandleFunc( - "/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetCommandById( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - c.HandleFunc( - "/"+NAME+"/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetCommandsByName( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) - - d := c.PathPrefix("/" + DEVICE).Subrouter() - d.HandleFunc( - "/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetCommandsByDeviceId( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - errorContainer.ErrorHandlerFrom(dic.Get)) - }).Methods(http.MethodGet) -} diff --git a/internal/core/metadata/utils.go b/internal/core/metadata/utils.go deleted file mode 100644 index b2b035be02..0000000000 --- a/internal/core/metadata/utils.go +++ /dev/null @@ -1,27 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * - * 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 metadata - -import ( - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/config" -) - -func checkMaxLimit(limit int, configuration *config.ConfigurationStruct) error { - if limit > configuration.Service.MaxResultCount { - return errors.NewErrLimitExceeded(limit) - } - - return nil -} diff --git a/internal/pkg/bootstrap/container/database.go b/internal/pkg/bootstrap/container/database.go deleted file mode 100644 index aa98c13d0c..0000000000 --- a/internal/pkg/bootstrap/container/database.go +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 container - -import ( - "github.com/edgexfoundry/edgex-go/internal/pkg/db/interfaces" - - "github.com/edgexfoundry/go-mod-bootstrap/v2/di" -) - -// DBClientInterfaceName contains the name of the interfaces.DBClient implementation in the DIC. -var DBClientInterfaceName = di.TypeInstanceToName((*interfaces.DBClient)(nil)) - -// DBClientFrom helper function queries the DIC and returns the interfaces.DBClient implementation. -func DBClientFrom(get di.Get) interfaces.DBClient { - return get(DBClientInterfaceName).(interfaces.DBClient) -} diff --git a/internal/pkg/bootstrap/handlers/database/database.go b/internal/pkg/bootstrap/handlers/database/database.go deleted file mode 100644 index 3bc303befd..0000000000 --- a/internal/pkg/bootstrap/handlers/database/database.go +++ /dev/null @@ -1,163 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 database - -import ( - "context" - "fmt" - "sync" - "time" - - "github.com/edgexfoundry/edgex-go/internal/pkg/bootstrap/container" - "github.com/edgexfoundry/edgex-go/internal/pkg/bootstrap/interfaces" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - dbInterfaces "github.com/edgexfoundry/edgex-go/internal/pkg/db/interfaces" - "github.com/edgexfoundry/edgex-go/internal/pkg/db/redis" - - bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/container" - "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/secret" - "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/startup" - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - "github.com/edgexfoundry/go-mod-bootstrap/v2/di" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/v2" -) - -// httpServer defines the contract used to determine whether or not the http httpServer is running. -type httpServer interface { - IsRunning() bool -} - -// Database contains references to dependencies required by the database bootstrap implementation. -type Database struct { - httpServer httpServer - database interfaces.Database - isCoreData bool -} - -// NewDatabase is a factory method that returns an initialized Database receiver struct. -func NewDatabase(httpServer httpServer, database interfaces.Database) Database { - return Database{ - httpServer: httpServer, - database: database, - isCoreData: false, - } -} - -// NewDatabaseForCoreData is a factory method that returns an initialized Database receiver struct. -func NewDatabaseForCoreData(httpServer httpServer, database interfaces.Database) Database { - return Database{ - httpServer: httpServer, - database: database, - isCoreData: true, - } -} - -// Return the dbClient interface -func (d Database) newDBClient( - lc logger.LoggingClient, - credentials bootstrapConfig.Credentials) (dbInterfaces.DBClient, error) { - - databaseInfo := d.database.GetDatabaseInfo()[v2.Primary] - switch databaseInfo.Type { - case db.RedisDB: - conf := db.Configuration{ - Host: databaseInfo.Host, - Port: databaseInfo.Port, - Password: credentials.Password, - } - - if d.isCoreData { - return redis.NewCoreDataClient(conf, lc) - } - return redis.NewClient(conf, lc) - default: - return nil, db.ErrUnsupportedDatabase - } -} - -// BootstrapHandler fulfills the BootstrapHandler contract and initializes the database. -func (d Database) BootstrapHandler( - ctx context.Context, - wg *sync.WaitGroup, - startupTimer startup.Timer, - dic *di.Container) bool { - - lc := bootstrapContainer.LoggingClientFrom(dic.Get) - secretProvider := bootstrapContainer.SecretProviderFrom(dic.Get) - - // get database credentials. - var credentials bootstrapConfig.Credentials - for startupTimer.HasNotElapsed() { - var err error - - secrets, err := secretProvider.GetSecret(d.database.GetDatabaseInfo()[v2.Primary].Type) - if err == nil { - credentials = bootstrapConfig.Credentials{ - Username: secrets[secret.UsernameKey], - Password: secrets[secret.PasswordKey], - } - - break - } - - lc.Warn(fmt.Sprintf("couldn't retrieve database credentials: %v", err.Error())) - startupTimer.SleepForInterval() - } - - // initialize database. - var dbClient dbInterfaces.DBClient - for startupTimer.HasNotElapsed() { - var err error - dbClient, err = d.newDBClient(lc, credentials) - if err == nil { - break - } - dbClient = nil - lc.Warn(fmt.Sprintf("couldn't create database client: %v", err.Error())) - startupTimer.SleepForInterval() - } - - if dbClient == nil { - lc.Error(fmt.Sprintf("failed to create database client in allotted time")) - return false - } - - dic.Update(di.ServiceConstructorMap{ - container.DBClientInterfaceName: func(get di.Get) interface{} { - return dbClient - }, - }) - - lc.Info("Database connected") - wg.Add(1) - go func() { - defer wg.Done() - - <-ctx.Done() - for { - // wait for httpServer to stop running (e.g. handling requests) before closing the database connection. - if d.httpServer.IsRunning() == false { - dbClient.CloseSession() - break - } - time.Sleep(time.Second) - } - lc.Info("Database disconnected") - }() - - return true -} diff --git a/internal/pkg/container/errorHandler.go b/internal/pkg/container/errorHandler.go deleted file mode 100644 index 6c8989b30f..0000000000 --- a/internal/pkg/container/errorHandler.go +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 container - -import ( - "github.com/edgexfoundry/edgex-go/internal/pkg/errorconcept" - - "github.com/edgexfoundry/go-mod-bootstrap/v2/di" -) - -// ErrorHandler contains the name of the errorconcept.Handler implementation in the DIC. -var ErrorHandlerName = di.TypeInstanceToName(errorconcept.Handler{}) - -// ErrorHandlerFrom helper function queries the DIC and returns the errorconcept.Handler implementation. -func ErrorHandlerFrom(get di.Get) *errorconcept.Handler { - return get(ErrorHandlerName).(*errorconcept.Handler) -} diff --git a/internal/pkg/correlation/models/event.go b/internal/pkg/correlation/models/event.go deleted file mode 100644 index 63ccb33e1e..0000000000 --- a/internal/pkg/correlation/models/event.go +++ /dev/null @@ -1,89 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 models - -import ( - "encoding/json" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type Event struct { - Bytes []byte // This will NOT be marshaled via the JSON below. It is only populated and read as an instance member. - CorrelationId string - Checksum string - contract.Event -} - -// Returns an instance of just the public contract portion of the model Event. -// I don't like returning a pointer from this method but I have to in order to -// satisfy the Filter, Format interfaces. -func (e Event) ToContract() *contract.Event { - event := contract.Event{ - ID: e.ID, - Pushed: e.Pushed, - Device: e.Device, - Created: e.Created, - Modified: e.Modified, - Origin: e.Origin, - } - - for _, r := range e.Readings { - event.Readings = append(event.Readings, r) - } - return &event -} - -func (e Event) MarshalJSON() ([]byte, error) { - test := struct { - CorrelationId *string `json:"correlation-id,omitempty"` - Checksum *string `json:"checksum,omitempty"` - ID *string `json:"id,omitempty"` - Pushed int64 `json:"pushed,omitempty"` - Device *string `json:"device,omitempty"` // Device identifier (name or id) - Created int64 `json:"created,omitempty"` - Modified int64 `json:"modified,omitempty"` - Origin int64 `json:"origin,omitempty"` - Readings []contract.Reading `json:"readings,omitempty"` // List of readings - }{ - Pushed: e.Pushed, - Created: e.Created, - Modified: e.Modified, - Origin: e.Origin, - } - - // Empty strings are null - if e.CorrelationId != "" { - test.CorrelationId = &e.CorrelationId - } - - if e.Checksum != "" { - test.Checksum = &e.Checksum - } - - if e.ID != "" { - test.ID = &e.ID - } - if e.Device != "" { - test.Device = &e.Device - } - - // Empty arrays are null - if len(e.Readings) > 0 { - test.Readings = e.Readings - } - - return json.Marshal(test) -} diff --git a/internal/pkg/db/db.go b/internal/pkg/db/db.go index 04d60e694e..c0ab16e0c4 100644 --- a/internal/pkg/db/db.go +++ b/internal/pkg/db/db.go @@ -16,40 +16,6 @@ package db import ( "errors" - "time" -) - -const ( - // Databases - - RedisDB = "redisdb" - - // Data - EventsCollection = "event" - ReadingsCollection = "reading" - ValueDescriptorCollection = "valueDescriptor" - - //Export - ExportCollection = "exportConfiguration" - - //Logging - LogsCollection = "logEntry" - - // Metadata - Device = "device" - DeviceProfile = "deviceProfile" - DeviceService = "deviceService" - Addressable = "addressable" - Command = "command" - DeviceReport = "deviceReport" - ProvisionWatcher = "provisionWatcher" - Interval = "interval" - IntervalAction = "intervalAction" - - // Notification - Notification = "notification" - Subscription = "subscription" - Transmission = "transmission" ) var ( @@ -72,7 +38,3 @@ type Configuration struct { Password string BatchSize int } - -func MakeTimestamp() int64 { - return time.Now().UnixNano() / int64(time.Millisecond) -} diff --git a/internal/pkg/db/interfaces/db.go b/internal/pkg/db/interfaces/db.go deleted file mode 100644 index b5d462007a..0000000000 --- a/internal/pkg/db/interfaces/db.go +++ /dev/null @@ -1,246 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 interfaces - -import ( - correlation "github.com/edgexfoundry/edgex-go/internal/pkg/correlation/models" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type DBClient interface { - CloseSession() - - /* - Events - NOTE: Readings that contain binary data will not be persisted. - */ - Events() ([]contract.Event, error) - EventsWithLimit(limit int) ([]contract.Event, error) - AddEvent(e correlation.Event) (string, error) - UpdateEvent(e correlation.Event) error - EventById(id string) (contract.Event, error) - EventsByChecksum(checksum string) ([]contract.Event, error) - EventCount() (int, error) - EventCountByDeviceId(id string) (int, error) - DeleteEventById(id string) error - DeleteEventsByDevice(deviceId string) (int, error) - EventsForDeviceLimit(id string, limit int) ([]contract.Event, error) - EventsForDevice(id string) ([]contract.Event, error) - EventsByCreationTime(startTime, endTime int64, limit int) ([]contract.Event, error) - EventsOlderThanAge(age int64) ([]contract.Event, error) - EventsPushed() ([]contract.Event, error) - ScrubAllEvents() error - - /* - Readings - NOTE: Readings that contain binary data will not be persisted. - */ - Readings() ([]contract.Reading, error) - AddReading(r contract.Reading) (string, error) - UpdateReading(r contract.Reading) error - ReadingById(id string) (contract.Reading, error) - ReadingCount() (int, error) - DeleteReadingById(id string) error - DeleteReadingsByDevice(deviceId string) error - ReadingsByDevice(id string, limit int) ([]contract.Reading, error) - ReadingsByValueDescriptor(name string, limit int) ([]contract.Reading, error) - ReadingsByValueDescriptorNames(names []string, limit int) ([]contract.Reading, error) - ReadingsByCreationTime(start, end int64, limit int) ([]contract.Reading, error) - ReadingsByDeviceAndValueDescriptor(deviceId, valueDescriptor string, limit int) ([]contract.Reading, error) - - /* - ValueDescriptors - */ - ValueDescriptors() ([]contract.ValueDescriptor, error) - AddValueDescriptor(v contract.ValueDescriptor) (string, error) - UpdateValueDescriptor(cvd contract.ValueDescriptor) error - DeleteValueDescriptorById(id string) error - ValueDescriptorByName(name string) (contract.ValueDescriptor, error) - ValueDescriptorsByName(names []string) ([]contract.ValueDescriptor, error) - ValueDescriptorById(id string) (contract.ValueDescriptor, error) - ValueDescriptorsByUomLabel(uomLabel string) ([]contract.ValueDescriptor, error) - ValueDescriptorsByLabel(label string) ([]contract.ValueDescriptor, error) - ValueDescriptorsByType(t string) ([]contract.ValueDescriptor, error) - ScrubAllValueDescriptors() error - - /* - Device Reports - */ - GetAllDeviceReports() ([]contract.DeviceReport, error) - GetDeviceReportByName(n string) (contract.DeviceReport, error) - GetDeviceReportByDeviceName(n string) ([]contract.DeviceReport, error) - GetDeviceReportById(id string) (contract.DeviceReport, error) - GetDeviceReportsByAction(n string) ([]contract.DeviceReport, error) - AddDeviceReport(d contract.DeviceReport) (string, error) - UpdateDeviceReport(dr contract.DeviceReport) error - DeleteDeviceReportById(id string) error - - /* - Devices - */ - GetAllDevices() ([]contract.Device, error) - AddDevice(d contract.Device, commands []contract.Command) (string, error) - UpdateDevice(d contract.Device) error - DeleteDeviceById(id string) error - GetDevicesByProfileId(id string) ([]contract.Device, error) - GetDeviceById(id string) (contract.Device, error) - GetDeviceByName(n string) (contract.Device, error) - GetDevicesByServiceId(id string) ([]contract.Device, error) - GetDevicesWithLabel(l string) ([]contract.Device, error) - - /* - Device Profiles - */ - GetAllDeviceProfiles() ([]contract.DeviceProfile, error) - GetDeviceProfileById(id string) (contract.DeviceProfile, error) - GetDeviceProfilesByModel(model string) ([]contract.DeviceProfile, error) - GetDeviceProfilesWithLabel(l string) ([]contract.DeviceProfile, error) - GetDeviceProfilesByManufacturerModel(man string, mod string) ([]contract.DeviceProfile, error) - GetDeviceProfilesByManufacturer(man string) ([]contract.DeviceProfile, error) - GetDeviceProfileByName(n string) (contract.DeviceProfile, error) - AddDeviceProfile(dp contract.DeviceProfile) (string, error) - UpdateDeviceProfile(dp contract.DeviceProfile) error - DeleteDeviceProfileById(id string) error - - /* - Addressables - */ - GetAddressables() ([]contract.Addressable, error) - UpdateAddressable(a contract.Addressable) error - GetAddressableById(id string) (contract.Addressable, error) - AddAddressable(a contract.Addressable) (string, error) - GetAddressableByName(n string) (contract.Addressable, error) - GetAddressablesByTopic(t string) ([]contract.Addressable, error) - GetAddressablesByPort(p int) ([]contract.Addressable, error) - GetAddressablesByPublisher(p string) ([]contract.Addressable, error) - GetAddressablesByAddress(add string) ([]contract.Addressable, error) - DeleteAddressableById(id string) error - - /* - Device Services - */ - GetDeviceServiceByName(n string) (contract.DeviceService, error) - GetDeviceServiceById(id string) (contract.DeviceService, error) - GetAllDeviceServices() ([]contract.DeviceService, error) - GetDeviceServicesByAddressableId(id string) ([]contract.DeviceService, error) - GetDeviceServicesWithLabel(l string) ([]contract.DeviceService, error) - AddDeviceService(ds contract.DeviceService) (string, error) - UpdateDeviceService(ds contract.DeviceService) error - DeleteDeviceServiceById(id string) error - - /* - Provision Watchers - */ - GetAllProvisionWatchers() (pw []contract.ProvisionWatcher, err error) - GetProvisionWatcherByName(n string) (pw contract.ProvisionWatcher, err error) - GetProvisionWatchersByIdentifier(k string, v string) (pw []contract.ProvisionWatcher, err error) - GetProvisionWatchersByServiceId(id string) (pw []contract.ProvisionWatcher, err error) - GetProvisionWatchersByProfileId(id string) (pw []contract.ProvisionWatcher, err error) - GetProvisionWatcherById(id string) (pw contract.ProvisionWatcher, err error) - AddProvisionWatcher(pw contract.ProvisionWatcher) (string, error) - UpdateProvisionWatcher(pw contract.ProvisionWatcher) error - DeleteProvisionWatcherById(id string) error - - /* - Commands - */ - GetAllCommands() ([]contract.Command, error) - GetCommandById(id string) (contract.Command, error) - GetCommandsByName(n string) ([]contract.Command, error) - GetCommandsByDeviceId(did string) ([]contract.Command, error) - GetCommandByNameAndDeviceId(cname string, did string) (contract.Command, error) - - ScrubMetadata() error - - /* - Notifications - */ - GetNotifications() ([]contract.Notification, error) - GetNotificationById(id string) (contract.Notification, error) - GetNotificationBySlug(slug string) (contract.Notification, error) - GetNotificationBySender(sender string, limit int) ([]contract.Notification, error) - GetNotificationsByLabels(labels []string, limit int) ([]contract.Notification, error) - GetNotificationsByStartEnd(start int64, end int64, limit int) ([]contract.Notification, error) - GetNotificationsByStart(start int64, limit int) ([]contract.Notification, error) - GetNotificationsByEnd(end int64, limit int) ([]contract.Notification, error) - GetNewNotifications(limit int) ([]contract.Notification, error) - GetNewNormalNotifications(limit int) ([]contract.Notification, error) - AddNotification(n contract.Notification) (string, error) - UpdateNotification(n contract.Notification) error - MarkNotificationProcessed(n contract.Notification) error - DeleteNotificationById(id string) error - DeleteNotificationBySlug(slug string) error - DeleteNotificationsOld(age int) error - - /* - Subscriptions - */ - GetSubscriptionBySlug(slug string) (contract.Subscription, error) - GetSubscriptionByCategories(categories []string) ([]contract.Subscription, error) - GetSubscriptionByLabels(labels []string) ([]contract.Subscription, error) - GetSubscriptionByCategoriesLabels(categories []string, labels []string) ([]contract.Subscription, error) - GetSubscriptionByReceiver(receiver string) ([]contract.Subscription, error) - GetSubscriptionById(id string) (contract.Subscription, error) - DeleteSubscriptionById(id string) error - AddSubscription(sub contract.Subscription) (string, error) - UpdateSubscription(sub contract.Subscription) error - DeleteSubscriptionBySlug(slug string) error - GetSubscriptions() ([]contract.Subscription, error) - - /* - Transmissions - */ - AddTransmission(t contract.Transmission) (string, error) - UpdateTransmission(t contract.Transmission) error - DeleteTransmission(age int64, status contract.TransmissionStatus) error - GetTransmissionById(id string) (contract.Transmission, error) - GetTransmissionsByNotificationSlug(slug string, limit int) ([]contract.Transmission, error) - GetTransmissionsByNotificationSlugAndStartEnd(slug string, start int64, end int64, limit int) ([]contract.Transmission, error) - GetTransmissionsByStartEnd(start int64, end int64, limit int) ([]contract.Transmission, error) - GetTransmissionsByStart(start int64, limit int) ([]contract.Transmission, error) - GetTransmissionsByEnd(end int64, limit int) ([]contract.Transmission, error) - GetTransmissionsByStatus(limit int, status contract.TransmissionStatus) ([]contract.Transmission, error) - - Cleanup() error - CleanupOld(age int) error - - /* - Intervals - */ - Intervals() ([]contract.Interval, error) - IntervalsWithLimit(limit int) ([]contract.Interval, error) - IntervalByName(name string) (contract.Interval, error) - IntervalById(id string) (contract.Interval, error) - AddInterval(interval contract.Interval) (string, error) - UpdateInterval(interval contract.Interval) error - DeleteIntervalById(id string) error - - /* - Interval Actions - */ - IntervalActions() ([]contract.IntervalAction, error) - IntervalActionsWithLimit(limit int) ([]contract.IntervalAction, error) - IntervalActionsByIntervalName(name string) ([]contract.IntervalAction, error) - IntervalActionsByTarget(name string) ([]contract.IntervalAction, error) - IntervalActionById(id string) (contract.IntervalAction, error) - IntervalActionByName(name string) (contract.IntervalAction, error) - AddIntervalAction(action contract.IntervalAction) (string, error) - UpdateIntervalAction(action contract.IntervalAction) error - DeleteIntervalActionById(id string) error - - ScrubAllIntervalActions() (int, error) - ScrubAllIntervals() (int, error) -} diff --git a/internal/pkg/db/redis/CONFIGURATION.md b/internal/pkg/db/redis/CONFIGURATION.md deleted file mode 100644 index bf7a3d18ff..0000000000 --- a/internal/pkg/db/redis/CONFIGURATION.md +++ /dev/null @@ -1,36 +0,0 @@ -# Configuring Microservices to use Redis - -As of EdgeX 2.0 (Ireland), all the microservices use Redis, which is the only supported database. - -## Requirements - -### Redis - -Redis can be run locally or as a Docker container. This document assumes the default port of 6379 is being used. If you are using a different port, massage these instructions appropriately. - -### EdgeX - -Follow the EdgeX build instructions or the EdgeX Docker Compose instructions to set yourself up for success. - -## Using Docker - -When starting EdgeX containers, Redis must be explicitly stated as the desired database for the microservices. - -```sh -make EDGEX_DB=redis run_docker -``` - -This will start Redis and all the microservices. - -## Using native microservices - -The `configuration.toml` files for Core Data, Core Metadata, Support Notifications, and Support Scheduler found in their respective `cmd//res` directory. - -For each of the microservices update the keys in the `Databases.Primary` table - -| Key | Value | -| ---- | ------- | -| Port | 6379 | -| Type | redisdb | - -Redis does not use the other keys in that table \ No newline at end of file diff --git a/internal/pkg/db/redis/README.md b/internal/pkg/db/redis/README.md deleted file mode 100644 index 8511974dbb..0000000000 --- a/internal/pkg/db/redis/README.md +++ /dev/null @@ -1,124 +0,0 @@ -# Redis Implementation Notes - -## Core Data - -### Events and Readings - -`pkg/models/event` is stored as a marshalled value using the event id as the key. - -Sorted sets are used to track events sorted by most recently added, created timestamp (both relative to all events and scoped to the source device), and the pushed timestamp. Readings are stored similarly in their own sorted sets. - -Given - -```go -var e Event -``` - -and - -```go -var e models.Event = models.Event{ - ID: "57ba04a1189b95b8afcdafd7", - Pushed: 1471806399999, - Device: "123456789", - Created: 1464039917100, - Modified: 1474774741088, - Origin: 1471806386919, - Event: "", - Reading: []Reading{ - Reading { - ID: "57b9fe08189b95b8afcdafd4", - Pushed: 0, - Created: 1471806984866, - Modified: 1471807130870, - Origin: 1471806386919, - Name: "temperature", - Value: "39" - }, - Reading { - ID: "57e745efe4b0ca8e6d7116d7", - Pushed: 0, - Created: 1474774511737, - Modified: 1474774511737, - Origin: 1471806386919, - Name: "power", - Value: "38" - }, - ... - } -} -``` - -then - -| Sorted Set Key | Score | Member | -| ------------------------ | ------------- | ------------------------ | -| event | 0 | 57ba04a1189b95b8afcdafd7 | -| event:created | 1464039917100 | 57ba04a1189b95b8afcdafd7 | -| event:pushed | 1471806399999 | 57ba04a1189b95b8afcdafd7 | -| event:device:123456789 | 1464039917100 | 57ba04a1189b95b8afcdafd7 | -| reading | 0 | 57b9fe08189b95b8afcdafd4 | -| reading:created | 1471806984866 | 57b9fe08189b95b8afcdafd4 | -| reading:device:123456789 | 1471806984866 | 57b9fe08189b95b8afcdafd4 | -| reading:name:temperature | 1471806984866 | 57b9fe08189b95b8afcdafd4 | -| reading | 0 | 57e745efe4b0ca8e6d7116d7 | -| reading:created | 1474774511737 | 57e745efe4b0ca8e6d7116d7 | -| reading:device:123456789 | 1474774511737 | 57e745efe4b0ca8e6d7116d7 | -| reading:name:power | 1474774511737 | 57e745efe4b0ca8e6d7116d7 | - -## Notification Service - -Each of Notification, Subscription, and Transmission objects are stored as a key/value pair where the key is the id of the object. The value is JSON marshalled string. - -Given the migration away from BSON ids and toward UUID, all generated ids are UUIDs. - -Changes: -* Add test at internal/pkg/db/test/db_notifications.go -* Create a separate folder at internal/pkg/db/redis_notification for migrating to master branch - * Implement db interface function according to internal/support/notifications/interfaces/db.go - -### Notifications - -Notifications are queried by Slug, sender, time, labels, and status. To support those queries a sorted set with the index (e.g. slug) as the score and the value as the key of the notification object in question. - -| Data Type | Key | Value | Score | -|-------------|------------------------------------|------------------------|-----------| -| Sets | Entity ID | Entity | | -| Sorted sets | "notification" | Entity ID | 0 | -| Hashes | "notification:slug" | SlugName and Entity ID | | -| Sorted sets | "notification:sender:{sender}" | Entity ID | 0 | -| Sorted sets | "notification:status:{status}" | Entity ID | 0 | -| Sorted sets | "notification:severity:{severity}" | Entity ID | 0 | -| Sorted sets | "notification:created" | Entity ID | Timestamp | -| Sorted sets | "notification:modified" | Entity ID | Timestamp | - -Where LABEL and STATUS are a specific label or status, respectively. - -### Subscriptions - -Subscriptions are queried by Slug, categories, labels, and receiver. - -| Data Type | Key | Value | Score | -|-------------|------------------------------------|------------------------|-----------| -| Sets | Entity ID | Entity | | -| Sorted sets | "subscription" | Entity ID | 0 | -| Hashes | "subscription:slug" | SlugName and Entity ID | | -| Sorted sets | "subscription:receiver:{receiver}" | Entity ID | 0 | -| Sets | "subscription:label:{label}" | Entity ID | | -| Sets | "subscription:category:{category}" | Entity ID | | - -Given the migration of EdgeX to UUID from BSON Id, UUID is used for the id in the Redis implementation of the Notification service. - -### Transmission - -| Data Type | Key | Value | Score | -|-------------|------------------------------------------|------------------------|-------------| -| Sets | Entity ID | Entity | | -| Sorted sets | "transmission" | Entity ID | 0 | -| Sorted sets | "transmission:slug:{slug}" | Entity ID | Timestamp | -| Sorted sets | "transmission:status:{status}" | Entity ID | ResendCount | -| Sorted sets | "transmission:resendcount:{resendcount}" | Entity ID | ResendCount | -| Sorted sets | "transmission:created" | Entity ID | Timestamp | -| Sorted sets | "transmission:modified" | Entity ID | Timestamp | - - diff --git a/internal/pkg/db/redis/client.go b/internal/pkg/db/redis/client.go index 1a0776c0c2..3a6c7734e8 100644 --- a/internal/pkg/db/redis/client.go +++ b/internal/pkg/db/redis/client.go @@ -14,7 +14,6 @@ package redis import ( - "errors" "fmt" "os" "sync" @@ -42,23 +41,6 @@ type CoreDataClient struct { logger logger.LoggingClient } -func NewCoreDataClient(config db.Configuration, logger logger.LoggingClient) (*CoreDataClient, error) { - var err error - dc := &CoreDataClient{} - dc.Client, err = NewClient(config, logger) - if err != nil { - return nil, err - } - dc.logger = logger - // Background process for deleting device readings and events. - // This only needs to be running for core-data since this is the service responsible for handling the deletion - // of events - go dc.AsyncDeleteEvents() - go dc.AsyncDeleteReadings() - - return dc, err -} - // Return a pointer to the Redis client func NewClient(config db.Configuration, lc logger.LoggingClient) (*Client, error) { once.Do(func() { @@ -125,13 +107,3 @@ func (c *Client) CloseSession() { currClient = nil once = sync.Once{} } - -// getConnection gets a connection from the pool -func getConnection() (conn redis.Conn, err error) { - if currClient == nil { - return nil, errors.New("No current Redis client: create a new client before getting a connection from it") - } - - conn = currClient.Pool.Get() - return conn, nil -} diff --git a/internal/pkg/db/redis/client_integration_test.go b/internal/pkg/db/redis/client_integration_test.go deleted file mode 100644 index 4151105545..0000000000 --- a/internal/pkg/db/redis/client_integration_test.go +++ /dev/null @@ -1,110 +0,0 @@ -// +build redisIntegration - -/******************************************************************************* - * Copyright 2018 Redis Labs Inc. - * - * 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. - *******************************************************************************/ - -// This test will only be executed if the tag redisRunning is added when running -// the tests with a command like: -// LD_LIBRARY_PATH=$GOROOT/src/github.com/redislab/eredis/redis/src go test -tags redisRunning - -// To test Redis, specify the a `Host` value as follows: -// * TCP connection: use the IP address or host name -// * Unix domain socket: use the path to the socket file (e.g. /tmp/redis.sock) -// * Embedded: leave empty - -package redis - -import ( - "errors" - "fmt" - "net/url" - "os" - "strconv" - "testing" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/stretchr/testify/require" - - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/pkg/db/test" -) - -const ( - RedisURLEnvName = "REDIS_SERVER_TEST" - DefaultRedisURL = "redis://localhost:6379" -) - -func TestRedisDB(t *testing.T) { - config, err := getDBConfiguration() - require.NoError(t, err) - rc, err := NewClient(config, logger.MockLogger{}) - if err != nil { - t.Fatalf("Could not connect with Redis: %v", err) - } - test.TestDataDB(t, rc) - rc.CloseSession() - - rc, err = NewClient(config, logger.MockLogger{}) - if err != nil { - t.Fatalf("Could not connect with Redis: %v", err) - } - test.TestMetadataDB(t, rc) - rc.CloseSession() - - rc, err = NewClient(config, logger.MockLogger{}) - if err != nil { - t.Fatalf("Could not connect with Redis: %v", err) - } - - rc, err = NewClient(config, logger.MockLogger{}) - if err != nil { - t.Fatalf("Could not connect with Redis: %v", err) - } - test.TestNotificationsDB(t, rc) - rc.CloseSession() - -} - -func BenchmarkRedisDB(b *testing.B) { - config, err := getDBConfiguration() - require.NoError(b, err) - rc, err := NewClient(config, logger.MockLogger{}) - if err != nil { - b.Fatalf("Could not connect with Redis: %v", err) - } - - test.BenchmarkDB(b, rc) -} - -func getDBConfiguration() (db.Configuration, error) { - redisURLString := os.Getenv(RedisURLEnvName) - if redisURLString == "" { - redisURLString = DefaultRedisURL - } - - redisURL, err := url.Parse(redisURLString) - if err != nil { - return db.Configuration{}, errors.New(fmt.Sprintf("unable to parse provided Redis URL '%s'", redisURLString)) - } - - portInt, err := strconv.Atoi(redisURL.Port()) - if err != nil { - return db.Configuration{}, errors.New(fmt.Sprintf("unable to parse provided Redis Port '%s'", redisURLString)) - } - - return db.Configuration{ - Host: redisURL.Hostname(), - Port: portInt, - }, nil -} diff --git a/internal/pkg/db/redis/data.go b/internal/pkg/db/redis/data.go deleted file mode 100644 index 5f32fe7759..0000000000 --- a/internal/pkg/db/redis/data.go +++ /dev/null @@ -1,1312 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Redis Labs Inc. - * - * 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 redis - -import ( - "fmt" - "time" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - correlation "github.com/edgexfoundry/edgex-go/internal/pkg/correlation/models" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - "github.com/gomodule/redigo/redis" - "github.com/google/uuid" - "github.com/imdario/mergo" -) - -var emptyBinaryValue = make([]byte, 0) - -// deleteReadingsChannel channel used to delete readings asynchronously -var deleteReadingsChannel = make(chan string, 50) -var deleteEventsChannel = make(chan string, 50) - -const ( - DeletedEventsCollection = "gc:" + db.EventsCollection - DeletedReadingsCollection = "gc:" + db.ReadingsCollection -) - -// ******************************* EVENTS ********************************** - -// ********************** EVENT FUNCTIONS ******************************* -// Return all the events -// Sort the events in descending order by ID -// UnexpectedError - failed to retrieve events from the database -func (c *Client) Events() (events []contract.Event, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.EventsCollection, 0, -1) - if err != nil { - if err != redis.ErrNil { - return events, err - } - } - events = make([]contract.Event, len(objects)) - err = unmarshalEvents(objects, events) - if err != nil { - return events, err - } - - return events, nil -} - -// Return events up to the number specified -// UnexpectedError - failed to retrieve events from the database -func (c *Client) EventsWithLimit(limit int) (events []contract.Event, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.EventsCollection, 0, limit-1) - if err != nil { - if err != redis.ErrNil { - return events, err - } - } - events = make([]contract.Event, len(objects)) - err = unmarshalEvents(objects, events) - if err != nil { - return events, err - } - - return events, nil -} - -// Add a new event -// UnexpectedError - failed to add to database -// NoValueDescriptor - no existing value descriptor for a reading in the event -func (c *Client) AddEvent(e correlation.Event) (id string, err error) { - conn := c.Pool.Get() - defer conn.Close() - - if e.ID != "" { - _, err = uuid.Parse(e.ID) - if err != nil { - return "", db.ErrInvalidObjectId - } - } - return addEvent(conn, e) -} - -// Update an event - do NOT update readings -// UnexpectedError - problem updating in database -// NotFound - no event with the ID was found -func (c *Client) UpdateEvent(e correlation.Event) (err error) { - conn := c.Pool.Get() - defer conn.Close() - - event := e.Event - - id := event.ID - - o, err := eventByID(conn, id) - if err != nil { - if err == redis.ErrNil { - return db.ErrNotFound - } - return err - } - - e.Modified = db.MakeTimestamp() - err = mergo.Merge(&event, o) - if err != nil { - return err - } - - err = deleteEvent(conn, id) - if err != nil { - return err - } - - _, err = addEvent(conn, e) - return err -} - -// Get an event by id -func (c *Client) EventById(id string) (event contract.Event, err error) { - conn := c.Pool.Get() - defer conn.Close() - - event, err = eventByID(conn, id) - if err != nil { - if err == redis.ErrNil { - return event, db.ErrNotFound - } - return event, err - } - - return event, nil -} - -// EventsByChecksum Get an event by checksum -func (c *Client) EventsByChecksum(checksum string) (events []contract.Event, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.EventsCollection+":checksum:"+checksum, 0, -1) - if err != nil { - if err != redis.ErrNil { - return events, err - } - } - events = make([]contract.Event, len(objects)) - err = unmarshalEvents(objects, events) - if err != nil { - return events, err - } - - if len(events) == 0 { - return events, db.ErrNotFound - } - - return events, nil -} - -// Get the number of events in Core Data -func (c *Client) EventCount() (count int, err error) { - conn := c.Pool.Get() - defer conn.Close() - - count, err = redis.Int(conn.Do("ZCARD", db.EventsCollection)) - if err != nil { - return 0, err - } - - return count, nil -} - -// Get the number of events in Core Data for the device specified by id -func (c *Client) EventCountByDeviceId(id string) (count int, err error) { - conn := c.Pool.Get() - defer conn.Close() - - count, err = redis.Int(conn.Do("ZCARD", db.EventsCollection+":device:"+id)) - if err != nil { - return 0, err - } - - return count, nil -} - -// Delete an event by ID. Readings are not deleted as this should be handled by the contract layer -// 404 - Event not found -// 503 - Unexpected problems -func (c *Client) DeleteEventById(id string) error { - conn := c.Pool.Get() - defer conn.Close() - - err := deleteEvent(conn, id) - if err != nil { - if err == redis.ErrNil { - return db.ErrNotFound - } - return err - } - - return nil -} - -// DeleteEventsByDevice Delete events and readings associated with the specified deviceID -func (c *Client) DeleteEventsByDevice(deviceId string) (int, error) { - err := c.DeleteReadingsByDevice(deviceId) - if err != nil { - return 0, err - } - - conn := c.Pool.Get() - defer conn.Close() - - ids, err := redis.Strings(conn.Do("ZRANGE", db.EventsCollection+":device:"+deviceId, 0, -1)) - if err != nil { - return 0, err - } - - err = conn.Send("MULTI") - if err != nil { - return 0, err - } - - for _, id := range ids { - err = conn.Send("RENAME", id, DeletedEventsCollection+":"+id) - if err != nil { - return 0, err - } - } - - err = conn.Send("EXEC") - deleteEventsChannel <- deviceId - - return len(ids), nil -} - -// AsyncDeleteEvents Handles the deletion of device events asynchronously. This function is expected to be running in -// a go-routine and works with the "DeleteEventsByDevice" function for better performance. -func (c *CoreDataClient) AsyncDeleteEvents() { - c.logger.Debug("Starting background event deletion process") - for { - select { - case device, ok := <-deleteEventsChannel: - if ok { - c.logger.Debug("Deleting event data for device: " + device) - startTime := time.Now() - c.deleteRenamedEvents(device) - c.logger.Debug(fmt.Sprintf("Deleted events for device: '%s', elapsed time: %s", device, time.Since(startTime))) - } - } - } -} - -// deleteRenamedEvents deletes all events associated with the specified device which have been marked for deletion. -// See the "DeleteEventsByDevice" function for details on the how events are marked for deletion(renamed) -func (c *Client) deleteRenamedEvents(device string) { - conn := c.Pool.Get() - defer conn.Close() - - ids, err := redis.Strings(conn.Do("ZRANGE", db.EventsCollection+":device:"+device, 0, -1)) - if err != nil { - c.loggingClient.Error("Unable to delete event:" + err.Error()) - return - } - - _, err = conn.Do("MULTI") - if err != nil { - c.loggingClient.Error("Unable to start transaction for deletion:" + err.Error()) - } - - for _, id := range ids { - _, err = conn.Do("GET", DeletedEventsCollection+":"+id) - if err != nil { - c.loggingClient.Error("Unable to obtain events marked for deletion:" + err.Error()) - } - } - events, err := redis.Strings(conn.Do("EXEC")) - - queriesInQueue := 0 - var e correlation.Event - _, err = conn.Do("MULTI") - if err != nil { - c.loggingClient.Error("Unable to start batch processing for event deletion:" + err.Error()) - } - - for _, event := range events { - err = unmarshalObject([]byte(event), &e) - if err != nil { - c.loggingClient.Error("Unable to marshal event: " + err.Error()) - } - _ = conn.Send("UNLINK", DeletedEventsCollection+":"+e.ID) - _ = conn.Send("ZREM", db.EventsCollection, e.ID) - _ = conn.Send("ZREM", db.EventsCollection+":created", e.ID) - _ = conn.Send("ZREM", db.EventsCollection+":device:"+e.Device, e.ID) - _ = conn.Send("ZREM", db.EventsCollection+":pushed", e.ID) - if e.Checksum != "" { - _ = conn.Send("ZREM", db.EventsCollection+":checksum:"+e.Checksum, 0) - } - - queriesInQueue++ - if queriesInQueue >= c.BatchSize { - _, err = conn.Do("EXEC") - queriesInQueue = 0 - if err != nil { - c.loggingClient.Error("Unable to execute batch deletion: " + err.Error()) - return - } - } - } - - if queriesInQueue > 0 { - _, err = conn.Do("EXEC") - - if err != nil { - c.loggingClient.Error("Unable to execute batch deletion: " + err.Error()) - } - } -} - -// Get a list of events based on the device id and limit -func (c *Client) EventsForDeviceLimit(id string, limit int) (events []contract.Event, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRevRange(conn, db.EventsCollection+":device:"+id, 0, limit-1) - if err != nil { - if err != redis.ErrNil { - return events, err - } - } - - events = make([]contract.Event, len(objects)) - err = unmarshalEvents(objects, events) - if err != nil { - return events, err - } - - return events, nil -} - -// Get a list of events based on the device id -func (c *Client) EventsForDevice(id string) (events []contract.Event, err error) { - events, err = c.EventsForDeviceLimit(id, 0) - if err != nil { - return nil, err - } - return events, nil -} - -// Delete all of the events by the device id (and the readings) -// DeleteEventsByDeviceId(id string) error - -// Return a list of events whos creation time is between startTime and endTime -// Limit the number of results by limit -func (c *Client) EventsByCreationTime(startTime, endTime int64, limit int) (events []contract.Event, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByScore(conn, db.EventsCollection+":created", startTime, endTime, limit) - if err != nil { - if err != redis.ErrNil { - return events, err - } - } - - events = make([]contract.Event, len(objects)) - err = unmarshalEvents(objects, events) - if err != nil { - return events, err - } - - return events, nil -} - -// Return a list of readings for a device filtered by the value descriptor and limited by the limit -// The readings are linked to the device through an event -func (c *Client) ReadingsByDeviceAndValueDescriptor(deviceId, valueDescriptor string, limit int) (readings []contract.Reading, err error) { - conn := c.Pool.Get() - defer conn.Close() - - if limit == 0 { - return readings, nil - } - - objects, err := getObjectsByValuesSorted( - conn, - limit, - db.ReadingsCollection+":device:"+deviceId, - db.ReadingsCollection+":name:"+valueDescriptor) - - if err != nil { - return readings, err - } - - readings = make([]contract.Reading, len(objects)) - for i, in := range objects { - err = unmarshalObject(in, &readings[i]) - if err != nil { - return readings, err - } - } - - return readings, nil - -} - -// Remove all the events that are older than the given age -// Return the number of events removed -// RemoveEventByAge(age int64) (int, error) - -// Get events that are older than a age -func (c *Client) EventsOlderThanAge(age int64) ([]contract.Event, error) { - expireDate := db.MakeTimestamp() - age - - return c.EventsByCreationTime(0, expireDate, 0) -} - -// Remove all the events that have been pushed -// func (dbc *DBClient) ScrubEvents()(int, error) - -// Get events that have been pushed (pushed field is not 0) -func (c *Client) EventsPushed() (events []contract.Event, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByScore(conn, db.EventsCollection+":pushed", 1, -1, 0) - if err != nil { - return events, err - } - - events = make([]contract.Event, len(objects)) - err = unmarshalEvents(objects, events) - if err != nil { - return events, err - } - - return events, nil -} - -// Delete all readings and events -func (c *Client) ScrubAllEvents() (err error) { - conn := c.Pool.Get() - defer conn.Close() - - err = unlinkCollection(conn, db.EventsCollection) - if err != nil { - return err - } - - err = unlinkCollection(conn, db.ReadingsCollection) - if err != nil { - return err - } - - return nil -} - -// ********************* READING FUNCTIONS ************************* -// Return a list of readings sorted by reading id -func (c *Client) Readings() (readings []contract.Reading, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.ReadingsCollection, 0, -1) - if err != nil { - return readings, err - } - - readings = make([]contract.Reading, len(objects)) - for i, in := range objects { - err = unmarshalObject(in, &readings[i]) - if err != nil { - return readings, err - } - } - - return readings, nil -} - -// Post a new reading -// Check if valuedescriptor exists in the database -func (c *Client) AddReading(r contract.Reading) (id string, err error) { - conn := c.Pool.Get() - defer conn.Close() - - if r.Id != "" { - _, err = uuid.Parse(r.Id) - if err != nil { - return "", db.ErrInvalidObjectId - } - } - return addReading(conn, true, r) -} - -// Update a reading -// 404 - reading cannot be found -// 409 - Value descriptor doesn't exist -// 503 - unknown issues -func (c *Client) UpdateReading(r contract.Reading) error { - conn := c.Pool.Get() - defer conn.Close() - - id := r.Id - o := contract.Reading{} - err := getObjectById(conn, id, unmarshalObject, &o) - if err != nil { - if err == redis.ErrNil { - return db.ErrNotFound - } - return err - } - - r.Modified = db.MakeTimestamp() - err = mergo.Merge(&r, o) - if err != nil { - return err - } - - err = deleteReading(conn, id) - if err != nil { - return err - } - - if r.Id != "" { - _, err = uuid.Parse(r.Id) - if err != nil { - return db.ErrInvalidObjectId - } - } - _, err = addReading(conn, true, r) - return err -} - -// Get a reading by ID -func (c *Client) ReadingById(id string) (reading contract.Reading, err error) { - conn := c.Pool.Get() - defer conn.Close() - - err = getObjectById(conn, id, unmarshalObject, &reading) - if err != nil { - if err == redis.ErrNil { - return reading, db.ErrNotFound - } - return reading, err - } - - return reading, nil -} - -// Get the number of readings in core data -func (c *Client) ReadingCount() (int, error) { - conn := c.Pool.Get() - defer conn.Close() - - count, err := redis.Int(conn.Do("ZCARD", db.ReadingsCollection)) - if err != nil { - return 0, err - } - - return count, nil -} - -// Delete a reading by ID -// 404 - can't find the reading with the given id -func (c *Client) DeleteReadingById(id string) error { - conn := c.Pool.Get() - defer conn.Close() - - err := deleteReading(conn, id) - if err != nil { - return err - } - - return nil -} - -// DeleteReadingsByDevice deletes readings associated with the specified device -func (c *Client) DeleteReadingsByDevice(device string) error { - conn := c.Pool.Get() - defer conn.Close() - - ids, err := redis.Strings(conn.Do("ZRANGE", db.ReadingsCollection+":device:"+device, 0, -1)) - if err != nil { - return err - } - - err = conn.Send("MULTI") - if err != nil { - return err - } - - for _, id := range ids { - err = conn.Send("RENAME", id, DeletedReadingsCollection+":"+id) - if err != nil { - return err - } - } - - err = conn.Send("EXEC") - deleteReadingsChannel <- device - - return nil -} - -// AsyncDeleteReadings Handles the deletion of device readings asynchronously. This function is expected to be running -// in a go-routine and works with the "DeleteReadingsByDevice" function for better performance. -func (c *CoreDataClient) AsyncDeleteReadings() { - c.logger.Debug("Starting background event deletion process") - for { - select { - case device, ok := <-deleteReadingsChannel: - if ok { - c.logger.Debug("Deleting reading data for device: " + device) - startTime := time.Now() - c.deleteRenamedReadings(device) - c.logger.Debug(fmt.Sprintf("Deleted readings for device: '%s', elapsed time: %s", device, time.Since(startTime))) - } - } - } -} - -// deleteRenamedReadings deletes all readings associated with the specified device which have been marked for deletion. -// See the "DeleteReadingsByDevice" function for details on the how readings are marked for deletion(renamed) -func (c *Client) deleteRenamedReadings(device string) { - conn := c.Pool.Get() - defer conn.Close() - - ids, err := redis.Strings(conn.Do("ZRANGE", db.ReadingsCollection+":device:"+device, 0, -1)) - if err != nil { - c.loggingClient.Error("Unable to delete reading:" + err.Error()) - return - } - - _, err = conn.Do("MULTI") - if err != nil { - c.loggingClient.Error("Unable to start transaction for deletion:" + err.Error()) - } - - for _, id := range ids { - _, err = conn.Do("GET", DeletedReadingsCollection+":"+id) - if err != nil { - c.loggingClient.Error("Unable to obtain readings marked for deletion:" + err.Error()) - } - } - readings, err := redis.Strings(conn.Do("EXEC")) - - queriesInQueue := 0 - var r contract.Reading - _, err = conn.Do("MULTI") - if err != nil { - c.loggingClient.Error("Unable to start batch processing for reading deletion:" + err.Error()) - } - - for _, reading := range readings { - err = unmarshalObject([]byte(reading), &r) - if err != nil { - c.loggingClient.Error("Unable to marshal reading: " + err.Error()) - } - _ = conn.Send("UNLINK", DeletedReadingsCollection+":"+r.Id) - _ = conn.Send("ZREM", db.ReadingsCollection, r.Id) - _ = conn.Send("ZREM", db.ReadingsCollection+":created", r.Id) - _ = conn.Send("ZREM", db.ReadingsCollection+":device:"+r.Device, r.Id) - _ = conn.Send("ZREM", db.ReadingsCollection+":name:"+r.Name, r.Id) - queriesInQueue++ - - if queriesInQueue >= c.BatchSize { - _, err = conn.Do("EXEC") - queriesInQueue = 0 - if err != nil { - c.loggingClient.Error("Unable to execute batch deletion: " + err.Error()) - return - } - } - } - - if queriesInQueue > 0 { - _, err = conn.Do("EXEC") - - if err != nil { - c.loggingClient.Error("Unable to execute batch deletion: " + err.Error()) - } - } -} - -// Return a list of readings for the given device (id or name) -// 404 - meta data checking enabled and can't find the device -// Sort the list of readings on creation date -func (c *Client) ReadingsByDevice(id string, limit int) (readings []contract.Reading, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRevRange(conn, db.ReadingsCollection+":device:"+id, 0, limit-1) - if err != nil { - if err != redis.ErrNil { - return readings, err - } - } - - readings = make([]contract.Reading, len(objects)) - for i, in := range objects { - err = unmarshalObject(in, &readings[i]) - if err != nil { - return readings, err - } - } - - return readings, nil -} - -// Return a list of readings for the given value descriptor -// 413 - the number exceeds the current max limit -func (c *Client) ReadingsByValueDescriptor(name string, limit int) (readings []contract.Reading, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.ReadingsCollection+":name:"+name, 0, limit-1) - if err != nil { - if err != redis.ErrNil { - return readings, err - } - } - - readings = make([]contract.Reading, len(objects)) - for i, in := range objects { - err = unmarshalObject(in, &readings[i]) - if err != nil { - return readings, err - } - } - - return readings, nil -} - -// Return a list of readings whose name is in the list of value descriptor names -func (c *Client) ReadingsByValueDescriptorNames(names []string, limit int) (readings []contract.Reading, err error) { - conn := c.Pool.Get() - defer conn.Close() - - if limit == 0 { - return readings, nil - } - limit-- - - for _, name := range names { - objects, err := getObjectsByRange(conn, db.ReadingsCollection+":name:"+name, 0, limit) - if err != nil { - if err != redis.ErrNil { - return readings, err - } - } - - t := make([]contract.Reading, len(objects)) - for i, in := range objects { - err = unmarshalObject(in, &t[i]) - if err != nil { - return readings, err - } - } - - readings = append(readings, t...) - - limit -= len(objects) - if limit < 0 { - break - } - } - - return readings, nil -} - -// Return a list of readings whos created time is between the start and end times -func (c *Client) ReadingsByCreationTime(start, end int64, limit int) (readings []contract.Reading, err error) { - conn := c.Pool.Get() - defer conn.Close() - - if limit == 0 { - return readings, nil - } - - objects, err := getObjectsByScore(conn, db.ReadingsCollection+":created", start, end, limit) - if err != nil { - return readings, err - } - - readings = make([]contract.Reading, len(objects)) - for i, in := range objects { - err = unmarshalObject(in, &readings[i]) - if err != nil { - return readings, err - } - } - - return readings, nil -} - -// ************************** VALUE DESCRIPTOR FUNCTIONS *************************** -// Add a value descriptor -// 409 - Formatting is bad or it is not unique -// 503 - Unexpected -// TODO: Check for valid printf formatting -func (c *Client) AddValueDescriptor(v contract.ValueDescriptor) (id string, err error) { - conn := c.Pool.Get() - defer conn.Close() - - if v.Id != "" { - _, err = uuid.Parse(v.Id) - if err != nil { - return "", db.ErrInvalidObjectId - } - } - return addValue(conn, v) -} - -// Return a list of all the value descriptors -// 513 Service Unavailable - database problems -func (c *Client) ValueDescriptors() (values []contract.ValueDescriptor, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.ValueDescriptorCollection, 0, -1) - if err != nil { - return values, err - } - - values = make([]contract.ValueDescriptor, len(objects)) - for i, in := range objects { - err = unmarshalObject(in, &values[i]) - if err != nil { - return values, err - } - } - - return values, nil -} - -// Update a value descriptor -// First use the ID for identification, then the name -// TODO: Check for the valid printf formatting -// 404 not found if the value descriptor cannot be found by the identifiers -func (c *Client) UpdateValueDescriptor(v contract.ValueDescriptor) error { - conn := c.Pool.Get() - defer conn.Close() - - id := v.Id - o, err := valueByName(conn, v.Name) - if err != nil && err != redis.ErrNil { - return err - } - if err == nil && o.Id != v.Id { - // IDs are different -> name not unique - return db.ErrNotUnique - } - - v.Modified = db.MakeTimestamp() - err = mergo.Merge(&v, o) - if err != nil { - return err - } - - err = deleteValue(conn, id) - if err != nil { - return err - } - - _, err = addValue(conn, v) - return err -} - -// Delete a value descriptor based on the ID -func (c *Client) DeleteValueDescriptorById(id string) error { - conn := c.Pool.Get() - defer conn.Close() - - err := deleteValue(conn, id) - if err != nil { - if err == redis.ErrNil { - return db.ErrNotFound - } - return err - } - return nil -} - -// Return a value descriptor based on the name -func (c *Client) ValueDescriptorByName(name string) (value contract.ValueDescriptor, err error) { - conn := c.Pool.Get() - defer conn.Close() - - value, err = valueByName(conn, name) - if err != nil { - if err == redis.ErrNil { - return value, db.ErrNotFound - } - return value, err - } - - return value, nil -} - -// Return value descriptors based on the names -func (c *Client) ValueDescriptorsByName(names []string) (values []contract.ValueDescriptor, err error) { - conn := c.Pool.Get() - defer conn.Close() - - for _, name := range names { - value, err := valueByName(conn, name) - if err != nil && err != redis.ErrNil { - return nil, err - } - - if err == nil { - values = append(values, value) - } - } - - return values, nil -} - -// Delete a valuedescriptor based on the name -// DeleteValueDescriptorByName(name string) error - -// Return a value descriptor based on the id -func (c *Client) ValueDescriptorById(id string) (value contract.ValueDescriptor, err error) { - conn := c.Pool.Get() - defer conn.Close() - - err = getObjectById(conn, id, unmarshalObject, &value) - if err == redis.ErrNil { - return value, db.ErrNotFound - } - if err != nil { - return value, err - } - - return value, nil -} - -// Return value descriptors based on the unit of measure label -func (c *Client) ValueDescriptorsByUomLabel(uomLabel string) (values []contract.ValueDescriptor, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.ValueDescriptorCollection+":uomlabel:"+uomLabel, 0, -1) - if err != nil { - if err != redis.ErrNil { - return values, err - } - } - - values = make([]contract.ValueDescriptor, len(objects)) - for i, in := range objects { - err = unmarshalObject(in, &values[i]) - if err != nil { - return values, err - } - } - - return values, nil -} - -// Return value descriptors based on the label -func (c *Client) ValueDescriptorsByLabel(label string) (values []contract.ValueDescriptor, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.ValueDescriptorCollection+":label:"+label, 0, -1) - if err != nil { - if err != redis.ErrNil { - return values, err - } - } - - values = make([]contract.ValueDescriptor, len(objects)) - for i, in := range objects { - err = unmarshalObject(in, &values[i]) - if err != nil { - return values, err - } - } - - return values, nil -} - -// Return a list of value descriptors based on their type -func (c *Client) ValueDescriptorsByType(t string) (values []contract.ValueDescriptor, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.ValueDescriptorCollection+":type:"+t, 0, -1) - if err != nil { - if err != redis.ErrNil { - return values, err - } - } - - values = make([]contract.ValueDescriptor, len(objects)) - for i, in := range objects { - err = unmarshalObject(in, &values[i]) - if err != nil { - return values, err - } - } - - return values, nil -} - -// Delete all value descriptors -func (c *Client) ScrubAllValueDescriptors() error { - conn := c.Pool.Get() - defer conn.Close() - - err := unlinkCollection(conn, db.ValueDescriptorCollection) - if err != nil { - return err - } - - return nil -} - -// ************************** HELPER FUNCTIONS *************************** -func addEvent(conn redis.Conn, e correlation.Event) (id string, err error) { - if e.ID == "" { - e.ID = uuid.New().String() - } - - m, err := marshalEvent(e) - if err != nil { - return "", err - } - - for i, reading := range e.Readings { - if reading.Device == "" { - e.Readings[i].Device = e.Device - } - } - - _ = conn.Send("MULTI") - _ = conn.Send("SET", e.ID, m) - _ = conn.Send("ZADD", db.EventsCollection, 0, e.ID) - _ = conn.Send("ZADD", db.EventsCollection+":created", e.Created, e.ID) - _ = conn.Send("ZADD", db.EventsCollection+":pushed", e.Pushed, e.ID) - _ = conn.Send("ZADD", db.EventsCollection+":device:"+e.Device, e.Created, e.ID) - if e.Checksum != "" { - _ = conn.Send("ZADD", db.EventsCollection+":checksum:"+e.Checksum, 0, e.ID) - } - - rids := make([]interface{}, len(e.Readings)*2+1) - rids[0] = db.EventsCollection + ":readings:" + e.ID - for i, r := range e.Readings { - r.Created = e.Created - r.Device = e.Device - - if r.Id != "" { - _, err = uuid.Parse(r.Id) - if err != nil { - return "", db.ErrInvalidObjectId - } - } - id, err = addReading(conn, false, r) - if err != nil { - return id, err - } - - e.Readings[i].Id = id - rids[i*2+1] = 0 - rids[i*2+2] = id - - } - if len(rids) > 1 { - _ = conn.Send("ZADD", rids...) - } - - _, err = conn.Do("EXEC") - return e.ID, err -} - -func deleteEvent(conn redis.Conn, id string) error { - o, err := eventByID(conn, id) - if err != nil { - if err == redis.ErrNil { - return db.ErrNotFound - } - return err - } - - // Obtain the checksum associated with the event. - // This is necessary since the helper functions result in an Event type which does not contain the checksum - // information, so we need to do a separate lookup to get the information. - checksum, err := checksumByEventID(conn, o.ID) - if err != nil { - return err - } - - _ = conn.Send("MULTI") - _ = conn.Send("UNLINK", id) - _ = conn.Send("UNLINK", db.EventsCollection+":readings:"+id) - _ = conn.Send("ZREM", db.EventsCollection, id) - _ = conn.Send("ZREM", db.EventsCollection+":created", id) - _ = conn.Send("ZREM", db.EventsCollection+":pushed", id) - _ = conn.Send("ZREM", db.EventsCollection+":device:"+o.Device, id) - if checksum != "" { - _ = conn.Send("ZREM", db.EventsCollection+":checksum:"+checksum, id) - } - - res, err := redis.Values(conn.Do("EXEC")) - if err != nil { - return err - } - exists, _ := redis.Bool(res[0], nil) - if !exists { - return redis.ErrNil - } - - // The Contract is responsible for data coherency thus there is no cleanup of specific readings - - return nil -} - -func eventByID(conn redis.Conn, id string) (event contract.Event, err error) { - obj, err := redis.Bytes(conn.Do("GET", id)) - if err == redis.ErrNil { - return event, db.ErrNotFound - } - if err != nil { - return event, err - } - - event, err = unmarshalEvent(obj) - if err != nil { - return event, err - } - - return event, err -} - -func eventByChecksum(conn redis.Conn, checksum string) (events []contract.Event, err error) { - objects, err := getObjectsByRange(conn, db.EventsCollection, 0, -1) - if err != nil { - if err != redis.ErrNil { - return events, err - } - } - events = make([]contract.Event, len(objects)) - err = unmarshalEvents(objects, events) - if err != nil { - return events, err - } - - return events, nil -} - -// checksumByEventID retrieves the checksum of the event associated with the provided Event ID. -// If there is no checksum associated with the Event then an empty string is returned. -func checksumByEventID(conn redis.Conn, id string) (string, error) { - obj, err := redis.Bytes(conn.Do("GET", id)) - if err == redis.ErrNil { - return "", db.ErrNotFound - } - if err != nil { - return "", err - } - - event, err := unmarshalRedisEvent(obj) - if err != nil { - return "", err - } - - return event.Checksum, nil -} - -// Add a reading to the database -func addReading(conn redis.Conn, tx bool, r contract.Reading) (id string, err error) { - // Clear the binary data since we do not want to persist binary data to save on memory. - // This is an explicit architectural decision. - r.BinaryValue = emptyBinaryValue - - if r.Created == 0 { - r.Created = db.MakeTimestamp() - } - - if r.Id == "" { - r.Id = uuid.New().String() - } - - m, err := marshalObject(r) - if err != nil { - return r.Id, err - } - - if tx { - _ = conn.Send("MULTI") - } - _ = conn.Send("SET", r.Id, m) - _ = conn.Send("ZADD", db.ReadingsCollection, 0, r.Id) - _ = conn.Send("ZADD", db.ReadingsCollection+":created", r.Created, r.Id) - _ = conn.Send("ZADD", db.ReadingsCollection+":device:"+r.Device, r.Created, r.Id) - _ = conn.Send("ZADD", db.ReadingsCollection+":name:"+r.Name, r.Created, r.Id) - if tx { - _, err = conn.Do("EXEC") - } - - return r.Id, err -} - -func deleteReading(conn redis.Conn, id string) error { - r := contract.Reading{} - err := getObjectById(conn, id, unmarshalObject, &r) - if err != nil { - return err - } - - _ = conn.Send("MULTI") - _ = conn.Send("UNLINK", id) - _ = conn.Send("ZREM", db.ReadingsCollection, id) - _ = conn.Send("ZREM", db.ReadingsCollection+":created", id) - _ = conn.Send("ZREM", db.ReadingsCollection+":device:"+r.Device, id) - _ = conn.Send("ZREM", db.ReadingsCollection+":name:"+r.Name, id) - _, err = conn.Do("EXEC") - if err != nil { - return err - } - - return nil -} - -func addValue(conn redis.Conn, v contract.ValueDescriptor) (id string, err error) { - if v.Created == 0 { - v.Created = db.MakeTimestamp() - } - - if v.Id == "" { - v.Id = uuid.New().String() - } - - exists, err := redis.Bool(conn.Do("HEXISTS", db.ValueDescriptorCollection+":name", v.Name)) - if err != nil { - return "", err - } else if exists { - return "", db.ErrNotUnique - } - - m, err := marshalObject(v) - if err != nil { - return "", err - } - - _ = conn.Send("MULTI") - _ = conn.Send("SET", v.Id, m) - _ = conn.Send("ZADD", db.ValueDescriptorCollection, 0, v.Id) - _ = conn.Send("HSET", db.ValueDescriptorCollection+":name", v.Name, v.Id) - _ = conn.Send("ZADD", db.ValueDescriptorCollection+":uomlabel:"+v.UomLabel, 0, v.Id) - _ = conn.Send("ZADD", db.ValueDescriptorCollection+":type:"+v.Type, 0, v.Id) - for _, label := range v.Labels { - _ = conn.Send("ZADD", db.ValueDescriptorCollection+":label:"+label, 0, v.Id) - } - _, err = conn.Do("EXEC") - return v.Id, err -} - -func deleteValue(conn redis.Conn, id string) error { - v := contract.ValueDescriptor{} - err := getObjectById(conn, id, unmarshalObject, &v) - if err != nil { - return err - } - - _ = conn.Send("MULTI") - _ = conn.Send("UNLINK", id) - _ = conn.Send("ZREM", db.ValueDescriptorCollection, id) - _ = conn.Send("HDEL", db.ValueDescriptorCollection+":name", v.Name) - _ = conn.Send("ZREM", db.ValueDescriptorCollection+":uomlabel:"+v.UomLabel, id) - _ = conn.Send("ZREM", db.ValueDescriptorCollection+":type:"+v.Type, id) - for _, label := range v.Labels { - _ = conn.Send("ZREM", db.ValueDescriptorCollection+":label:"+label, 0, id) - } - _, err = conn.Do("EXEC") - if err != nil { - return err - } - return nil -} - -func valueByName(conn redis.Conn, name string) (value contract.ValueDescriptor, err error) { - id, err := redis.String(conn.Do("HGET", db.ValueDescriptorCollection+":name", name)) - if err != nil { - return value, err - } - - err = getObjectById(conn, id, unmarshalObject, &value) - if err != nil { - return value, err - } - - return value, nil -} diff --git a/internal/pkg/db/redis/device.go b/internal/pkg/db/redis/device.go deleted file mode 100644 index 7bb09bc735..0000000000 --- a/internal/pkg/db/redis/device.go +++ /dev/null @@ -1,102 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Redis Labs Inc. - * - * 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 redis - -import ( - "fmt" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type redisDevice struct { - contract.DescribedObject - Id string - Name string - AdminState contract.AdminState - OperatingState contract.OperatingState - Protocols map[string]contract.ProtocolProperties - AutoEvents []contract.AutoEvent - LastConnected int64 - LastReported int64 - Labels []string - Location interface{} - Service string - Profile string - ProfileName string -} - -func marshalDevice(d contract.Device) (out []byte, err error) { - s := redisDevice{ - DescribedObject: d.DescribedObject, - Id: d.Id, - Name: d.Name, - AdminState: d.AdminState, - OperatingState: d.OperatingState, - Protocols: d.Protocols, - AutoEvents: d.AutoEvents, - LastConnected: d.LastConnected, - LastReported: d.LastReported, - Labels: d.Labels, - Location: d.Location, - Service: d.Service.Id, - Profile: d.Profile.Id, - ProfileName: d.Profile.Name, - } - - return marshalObject(s) -} - -func unmarshalDevice(o []byte, d interface{}) (err error) { - var s redisDevice - - err = unmarshalObject(o, &s) - if err != nil { - return err - } - - switch x := d.(type) { - case *contract.Device: - x.DescribedObject = s.DescribedObject - x.Id = s.Id - x.Name = s.Name - x.AdminState = s.AdminState - x.Protocols = s.Protocols - x.AutoEvents = s.AutoEvents - x.OperatingState = s.OperatingState - x.LastConnected = s.LastConnected - x.LastReported = s.LastReported - x.Labels = s.Labels - x.Location = s.Location - - conn, err := getConnection() - if err != nil { - return err - } - defer conn.Close() - - err = getObjectById(conn, s.Service, unmarshalDeviceService, &x.Service) - if err != nil { - return err - } - - err = getObjectById(conn, s.Profile, unmarshalDeviceProfile, &x.Profile) - if err != nil { - return err - } - - return nil - default: - return fmt.Errorf("Can only unmarshal into a *Device, got %T", x) - } -} diff --git a/internal/pkg/db/redis/device_profile.go b/internal/pkg/db/redis/device_profile.go deleted file mode 100644 index d6fed2c6c9..0000000000 --- a/internal/pkg/db/redis/device_profile.go +++ /dev/null @@ -1,72 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Redis Labs Inc. - * - * 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 redis - -import ( - "fmt" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type redisDeviceProfile struct { - contract.DescribedObject - Id string - Name string - Manufacturer string - Model string - Labels []string - DeviceResources []contract.DeviceResource - DeviceCommands []contract.ProfileResource - CoreCommands []contract.Command -} - -func marshalDeviceProfile(dp contract.DeviceProfile) (out []byte, err error) { - s := redisDeviceProfile{ - DescribedObject: dp.DescribedObject, - Id: dp.Id, - Name: dp.Name, - Manufacturer: dp.Manufacturer, - Model: dp.Model, - Labels: dp.Labels, - DeviceResources: dp.DeviceResources, - DeviceCommands: dp.DeviceCommands, - CoreCommands: dp.CoreCommands, - } - - return marshalObject(s) -} - -func unmarshalDeviceProfile(o []byte, dp interface{}) (err error) { - var s redisDeviceProfile - - err = unmarshalObject(o, &s) - if err != nil { - return err - } - - switch x := dp.(type) { - case *contract.DeviceProfile: - x.DescribedObject = s.DescribedObject - x.Id = s.Id - x.Name = s.Name - x.Manufacturer = s.Manufacturer - x.Model = s.Model - x.Labels = s.Labels - x.DeviceResources = s.DeviceResources - x.DeviceCommands = s.DeviceCommands - x.CoreCommands = s.CoreCommands - return nil - default: - return fmt.Errorf("Can only unmarshal into a *DeviceProfile, got %T", x) - } -} diff --git a/internal/pkg/db/redis/device_service.go b/internal/pkg/db/redis/device_service.go deleted file mode 100644 index 652e19c468..0000000000 --- a/internal/pkg/db/redis/device_service.go +++ /dev/null @@ -1,79 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Redis Labs Inc. - * - * 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 redis - -import ( - "fmt" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type redisDeviceService struct { - contract.DescribedObject - Id string - Name string - LastConnected int64 - LastReported int64 - OperatingState contract.OperatingState - Addressable string - Labels []string - AdminState contract.AdminState -} - -func marshalDeviceService(ds contract.DeviceService) (out []byte, err error) { - s := redisDeviceService{ - DescribedObject: ds.DescribedObject, - Id: ds.Id, - Name: ds.Name, - LastConnected: ds.LastConnected, - LastReported: ds.LastReported, - OperatingState: ds.OperatingState, - Addressable: ds.Addressable.Id, - Labels: ds.Labels, - AdminState: ds.AdminState, - } - - return marshalObject(s) -} - -func unmarshalDeviceService(o []byte, ds interface{}) (err error) { - var s redisDeviceService - - err = unmarshalObject(o, &s) - if err != nil { - return err - } - - switch x := ds.(type) { - case *contract.DeviceService: - x.DescribedObject = s.DescribedObject - x.Id = s.Id - x.Name = s.Name - x.LastConnected = s.LastConnected - x.LastReported = s.LastReported - x.OperatingState = s.OperatingState - x.Labels = s.Labels - x.AdminState = s.AdminState - conn, err := getConnection() - if err != nil { - return err - } - defer conn.Close() - - err = getObjectById(conn, s.Addressable, unmarshalObject, &x.Addressable) - return err - default: - return fmt.Errorf("Can only unmarshal into a *DeviceService, got %T", x) - } -} diff --git a/internal/pkg/db/redis/event.go b/internal/pkg/db/redis/event.go deleted file mode 100644 index b468b492dd..0000000000 --- a/internal/pkg/db/redis/event.go +++ /dev/null @@ -1,117 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Redis Labs Inc. - * - * 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 redis - -import ( - "encoding/json" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - correlation "github.com/edgexfoundry/edgex-go/internal/pkg/correlation/models" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - - "github.com/gomodule/redigo/redis" -) - -type redisEvent struct { - ID string - Checksum string - Pushed int64 - Device string - Created int64 - Modified int64 - Origin int64 - Tags map[string]string -} - -func marshalEvent(event correlation.Event) (out []byte, err error) { - s := redisEvent{ - ID: event.ID, - Checksum: event.Checksum, - Pushed: event.Pushed, - Device: event.Device, - Created: event.Created, - Modified: event.Modified, - Origin: event.Origin, - Tags: event.Tags, - } - - return marshalObject(s) -} - -func unmarshalEvents(objects [][]byte, events []contract.Event) (err error) { - for i, o := range objects { - if len(o) > 0 { - events[i], err = unmarshalEvent(o) - if err != nil { - return err - } - } - } - - return nil -} - -func unmarshalEvent(o []byte) (contract.Event, error) { - s, err := unmarshalRedisEvent(o) - if err != nil { - return contract.Event{}, err - } - - event := contract.Event{ - ID: s.ID, - Pushed: s.Pushed, - Device: s.Device, - Created: s.Created, - Modified: s.Modified, - Origin: s.Origin, - Tags: s.Tags, - } - - conn, err := getConnection() - if err != nil { - return contract.Event{}, err - } - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.EventsCollection+":readings:"+s.ID, 0, -1) - if err != nil { - if err != redis.ErrNil { - return contract.Event{}, err - } - } - - event.Readings = make([]contract.Reading, len(objects)) - - for i, in := range objects { - err = unmarshalObject(in, &event.Readings[i]) - if err != nil { - return contract.Event{}, err - } - } - - return event, nil -} - -// unmarshalRedisEvent constructs a redisEvent type from the provided bytes. -func unmarshalRedisEvent(o []byte) (redisEvent, error) { - var event redisEvent - - err := json.Unmarshal(o, &event) - if err != nil { - return redisEvent{}, err - } - - return event, nil -} diff --git a/internal/pkg/db/redis/metadata.go b/internal/pkg/db/redis/metadata.go deleted file mode 100644 index 961df9eb0e..0000000000 --- a/internal/pkg/db/redis/metadata.go +++ /dev/null @@ -1,1260 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Redis Labs Inc. - * - * 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 redis - -import ( - "errors" - "fmt" - "strconv" - - types "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/gomodule/redigo/redis" - "github.com/google/uuid" -) - -// /* ----------------------Device Report --------------------------*/ -func (c *Client) GetAllDeviceReports() ([]contract.DeviceReport, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.DeviceReport, 0, -1) - if err != nil { - return []contract.DeviceReport{}, err - } - - d := make([]contract.DeviceReport, len(objects)) - for i, object := range objects { - err = unmarshalObject(object, &d[i]) - if err != nil { - return []contract.DeviceReport{}, err - } - } - - return d, nil -} - -func (c *Client) GetDeviceReportByName(n string) (contract.DeviceReport, error) { - conn := c.Pool.Get() - defer conn.Close() - - d := contract.DeviceReport{} - err := getObjectByHash(conn, db.DeviceReport+":name", n, unmarshalObject, &d) - - return d, err -} - -func (c *Client) getDeviceReports(n string) ([]contract.DeviceReport, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByValue(conn, n) - if err != nil { - return []contract.DeviceReport{}, err - } - - d := make([]contract.DeviceReport, len(objects)) - for i, object := range objects { - err = unmarshalObject(object, &d[i]) - if err != nil { - return []contract.DeviceReport{}, err - } - } - - return d, nil -} - -func (c *Client) GetDeviceReportByDeviceName(n string) ([]contract.DeviceReport, error) { - return c.getDeviceReports(db.DeviceReport + ":device:" + n) -} - -func (c *Client) GetDeviceReportById(id string) (contract.DeviceReport, error) { - conn := c.Pool.Get() - defer conn.Close() - - var d contract.DeviceReport - err := getObjectById(conn, id, unmarshalObject, &d) - return d, err -} - -func (c *Client) GetDeviceReportsByScheduleEventName(n string) ([]contract.DeviceReport, error) { - return c.getDeviceReports(db.DeviceReport + ":scheduleevent:" + n) -} - -func (c *Client) GetDeviceReportsByAction(n string) ([]contract.DeviceReport, error) { - return c.getDeviceReports(db.DeviceReport + ":action:" + n) -} - -func (c *Client) AddDeviceReport(d contract.DeviceReport) (string, error) { - conn := c.Pool.Get() - defer conn.Close() - - return addDeviceReport(conn, d) -} - -func (c *Client) UpdateDeviceReport(dr contract.DeviceReport) error { - conn := c.Pool.Get() - defer conn.Close() - - err := deleteDeviceReport(conn, dr.Id) - if err != nil { - return err - } - - _, err = addDeviceReport(conn, dr) - return err -} - -func (c *Client) DeleteDeviceReportById(id string) error { - conn := c.Pool.Get() - defer conn.Close() - - return deleteDeviceReport(conn, id) -} - -func addDeviceReport(conn redis.Conn, dr contract.DeviceReport) (string, error) { - exists, err := redis.Bool(conn.Do("HEXISTS", db.DeviceReport+":name", dr.Name)) - if err != nil { - return "", err - } else if exists { - return "", db.ErrNotUnique - } - - _, err = uuid.Parse(dr.Id) - if err != nil { - dr.Id = uuid.New().String() - } - id := dr.Id - - ts := db.MakeTimestamp() - if dr.Created == 0 { - dr.Created = ts - } - dr.Modified = ts - - m, err := marshalObject(dr) - if err != nil { - return "", err - } - - _ = conn.Send("MULTI") - _ = conn.Send("SET", id, m) - _ = conn.Send("ZADD", db.DeviceReport, 0, id) - _ = conn.Send("SADD", db.DeviceReport+":device:"+dr.Device, id) - _ = conn.Send("SADD", db.DeviceReport+":action:"+dr.Action, id) - _ = conn.Send("HSET", db.DeviceReport+":name", dr.Name, id) - _, err = conn.Do("EXEC") - return id, err -} - -func deleteDeviceReport(conn redis.Conn, id string) error { - object, err := redis.Bytes(conn.Do("GET", id)) - if err == redis.ErrNil { - return db.ErrNotFound - } else if err != nil { - return err - } - - dr := contract.DeviceReport{} - _ = unmarshalObject(object, &dr) - - _ = conn.Send("MULTI") - _ = conn.Send("DEL", id) - _ = conn.Send("ZREM", db.DeviceReport, id) - _ = conn.Send("SREM", db.DeviceReport+":device:"+dr.Device, id) - _ = conn.Send("SREM", db.DeviceReport+":action:"+dr.Action, id) - _ = conn.Send("HDEL", db.DeviceReport+":name", dr.Name) - _, err = conn.Do("EXEC") - return err -} - -// /* ----------------------------- Device ---------------------------------- */ -func (c *Client) AddDevice(d contract.Device, commands []contract.Command) (string, error) { - conn := c.Pool.Get() - defer conn.Close() - - return addDevice(conn, d, commands) -} - -func (c *Client) UpdateDevice(d contract.Device) error { - conn := c.Pool.Get() - defer conn.Close() - - err := deleteDevice(conn, d.Id) - if err != nil { - return err - } - - _, err = addDevice(conn, d, d.Profile.CoreCommands) - return err -} - -func (c *Client) DeleteDeviceById(id string) error { - conn := c.Pool.Get() - defer conn.Close() - - return deleteDevice(conn, id) -} - -func (c *Client) GetAllDevices() ([]contract.Device, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.Device, 0, -1) - if err != nil { - return []contract.Device{}, err - } - - d := make([]contract.Device, len(objects)) - for i, object := range objects { - err = unmarshalDevice(object, &d[i]) - if err != nil { - return []contract.Device{}, err - } - } - - return d, nil -} - -func (c *Client) GetDevicesByProfileId(id string) ([]contract.Device, error) { - return c.getDevicesByValue(db.Device + ":profile:" + id) -} - -func (c *Client) GetDeviceById(id string) (contract.Device, error) { - conn := c.Pool.Get() - defer conn.Close() - - var d contract.Device - err := getObjectById(conn, id, unmarshalDevice, &d) - return d, err -} - -func (c *Client) GetDeviceByName(n string) (contract.Device, error) { - conn := c.Pool.Get() - defer conn.Close() - - var d contract.Device - err := getObjectByHash(conn, db.Device+":name", n, unmarshalDevice, &d) - return d, err -} - -func (c *Client) GetDevicesByServiceId(id string) ([]contract.Device, error) { - return c.getDevicesByValue(db.Device + ":service:" + id) -} - -func (c *Client) GetDevicesWithLabel(l string) ([]contract.Device, error) { - return c.getDevicesByValue(db.Device + ":label:" + l) -} - -func (c *Client) getDevicesByValue(v string) ([]contract.Device, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByValue(conn, v) - if err != nil { - return []contract.Device{}, err - } - - d := make([]contract.Device, len(objects)) - for i, object := range objects { - err = unmarshalDevice(object, &d[i]) - if err != nil { - return []contract.Device{}, err - } - } - - return d, nil -} - -func addDevice(conn redis.Conn, d contract.Device, commands []contract.Command) (string, error) { - exists, err := redis.Bool(conn.Do("HEXISTS", db.Device+":name", d.Name)) - if err != nil { - return "", err - } else if exists { - return "", db.ErrNotUnique - } - - _, err = uuid.Parse(d.Id) - if err != nil { - d.Id = uuid.New().String() - } - id := d.Id - - ts := db.MakeTimestamp() - if d.Created == 0 { - d.Created = ts - } - d.Modified = ts - - m, err := marshalDevice(d) - if err != nil { - return "", err - } - - _ = conn.Send("MULTI") - _ = conn.Send("SET", id, m) - _ = conn.Send("ZADD", db.Device, 0, id) - _ = conn.Send("HSET", db.Device+":name", d.Name, id) - _ = conn.Send("SADD", db.Device+":service:"+d.Service.Id, id) - _ = conn.Send("SADD", db.Device+":profile:"+d.Profile.Id, id) - for _, label := range d.Labels { - _ = conn.Send("SADD", db.Device+":label:"+label, id) - } - //add commands - for _, c := range commands { - cid, err := addCommand(conn, false, c) - if err != nil { - return "", err - } - _ = conn.Send("SADD", db.Command+":device:"+id, cid) - } - _, err = conn.Do("EXEC") - return id, err -} - -func deleteDevice(conn redis.Conn, id string) error { - object, err := redis.Bytes(conn.Do("GET", id)) - if err == redis.ErrNil { - return db.ErrNotFound - } else if err != nil { - return err - } - - d := contract.Device{} - _ = unmarshalDevice(object, &d) - - cmds, err := getCommandsByDeviceId(conn, id) - if err != nil { - return err - } - - _ = conn.Send("MULTI") - _ = conn.Send("DEL", id) - _ = conn.Send("ZREM", db.Device, id) - _ = conn.Send("HDEL", db.Device+":name", d.Name) - _ = conn.Send("SREM", db.Device+":service:"+d.Service.Id, id) - _ = conn.Send("SREM", db.Device+":profile:"+d.Profile.Id, id) - for _, label := range d.Labels { - _ = conn.Send("SREM", db.Device+":label:"+label, id) - } - - for _, c := range cmds { - deleteCommand(conn, c) - _ = conn.Send("SREM", db.Command+":device:"+id, c.Id) - } - - _, err = conn.Do("EXEC") - return err -} - -// /* -----------------------------Device Profile -----------------------------*/ -func (c *Client) GetDeviceProfileById(id string) (contract.DeviceProfile, error) { - conn := c.Pool.Get() - defer conn.Close() - - var d contract.DeviceProfile - err := getObjectById(conn, id, unmarshalDeviceProfile, &d) - return d, err -} - -func (c *Client) GetAllDeviceProfiles() ([]contract.DeviceProfile, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.DeviceProfile, 0, -1) - if err != nil { - return []contract.DeviceProfile{}, err - } - - dp := make([]contract.DeviceProfile, len(objects)) - for i, object := range objects { - err = unmarshalDeviceProfile(object, &dp[i]) - if err != nil { - return []contract.DeviceProfile{}, err - } - } - - return dp, nil -} - -func (c *Client) GetDeviceProfilesByModel(model string) ([]contract.DeviceProfile, error) { - return c.getDeviceProfilesByValues(db.DeviceProfile + ":model:" + model) -} - -func (c *Client) GetDeviceProfilesWithLabel(l string) ([]contract.DeviceProfile, error) { - return c.getDeviceProfilesByValues(db.DeviceProfile + ":label:" + l) -} - -func (c *Client) GetDeviceProfilesByManufacturerModel(man string, mod string) ([]contract.DeviceProfile, error) { - return c.getDeviceProfilesByValues(db.DeviceProfile+":manufacturer:"+man, db.DeviceProfile+":model:"+mod) -} - -func (c *Client) GetDeviceProfilesByManufacturer(man string) ([]contract.DeviceProfile, error) { - return c.getDeviceProfilesByValues(db.DeviceProfile + ":manufacturer:" + man) -} - -func (c *Client) GetDeviceProfileByName(n string) (contract.DeviceProfile, error) { - conn := c.Pool.Get() - defer conn.Close() - - var dp contract.DeviceProfile - err := getObjectByHash(conn, db.DeviceProfile+":name", n, unmarshalDeviceProfile, &dp) - return dp, err -} - -// Get device profiles with the passed query -func (c *Client) getDeviceProfilesByValues(vals ...string) ([]contract.DeviceProfile, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByValues(conn, vals...) - if err != nil { - return []contract.DeviceProfile{}, err - } - - dp := make([]contract.DeviceProfile, len(objects)) - for i, object := range objects { - err = unmarshalDeviceProfile(object, &dp[i]) - if err != nil { - return []contract.DeviceProfile{}, err - } - } - - return dp, nil -} - -func (c *Client) AddDeviceProfile(dp contract.DeviceProfile) (string, error) { - conn := c.Pool.Get() - defer conn.Close() - - return addDeviceProfile(conn, dp) -} - -func (c *Client) UpdateDeviceProfile(dp contract.DeviceProfile) error { - conn := c.Pool.Get() - defer conn.Close() - - err := deleteDeviceProfile(conn, dp.Id) - if err != nil { - return err - } - - _, err = addDeviceProfile(conn, dp) - return err -} - -func (c *Client) DeleteDeviceProfileById(id string) error { - conn := c.Pool.Get() - defer conn.Close() - - return deleteDeviceProfile(conn, id) -} - -func addDeviceProfile(conn redis.Conn, dp contract.DeviceProfile) (string, error) { - exists, err := redis.Bool(conn.Do("HEXISTS", db.DeviceProfile+":name", dp.Name)) - if err != nil { - return "", err - } else if exists { - return "", db.ErrNotUnique - } - - _, err = uuid.Parse(dp.Id) - if err != nil { - dp.Id = uuid.New().String() - } - id := dp.Id - - ts := db.MakeTimestamp() - if dp.Created == 0 { - dp.Created = ts - } - dp.Modified = ts - - m, err := marshalDeviceProfile(dp) - if err != nil { - return "", err - } - - _ = conn.Send("MULTI") - _ = conn.Send("SET", id, m) - _ = conn.Send("ZADD", db.DeviceProfile, 0, id) - _ = conn.Send("HSET", db.DeviceProfile+":name", dp.Name, id) - _ = conn.Send("SADD", db.DeviceProfile+":manufacturer:"+dp.Manufacturer, id) - _ = conn.Send("SADD", db.DeviceProfile+":model:"+dp.Model, id) - for _, label := range dp.Labels { - _ = conn.Send("SADD", db.DeviceProfile+":label:"+label, id) - } - - _, err = conn.Do("EXEC") - return id, err -} - -func deleteDeviceProfile(conn redis.Conn, id string) error { - object, err := redis.Bytes(conn.Do("GET", id)) - if err == redis.ErrNil { - return db.ErrNotFound - } else if err != nil { - return err - } - - dp := contract.DeviceProfile{} - _ = unmarshalDeviceProfile(object, &dp) - - _ = conn.Send("MULTI") - _ = conn.Send("DEL", id) - _ = conn.Send("ZREM", db.DeviceProfile, id) - _ = conn.Send("HDEL", db.DeviceProfile+":name", dp.Name) - _ = conn.Send("SREM", db.DeviceProfile+":manufacturer:"+dp.Manufacturer, id) - _ = conn.Send("SREM", db.DeviceProfile+":model:"+dp.Model, id) - for _, label := range dp.Labels { - _ = conn.Send("SREM", db.DeviceProfile+":label:"+label, id) - } - - _, err = conn.Do("EXEC") - return err -} - -/* -----------------------------------Addressable -------------------------- */ -//func (c *Client) UpdateAddressable(updated *contract.Addressable, orig *contract.Addressable) error { -func (c *Client) UpdateAddressable(a contract.Addressable) error { - conn := c.Pool.Get() - defer conn.Close() - - err := deleteAddressable(conn, a.Id) - if err != nil { - return err - } - - _, err = addAddressable(conn, a) - return err -} - -func (c *Client) AddAddressable(a contract.Addressable) (string, error) { - conn := c.Pool.Get() - defer conn.Close() - - return addAddressable(conn, a) -} - -func (c *Client) GetAddressableById(id string) (contract.Addressable, error) { - conn := c.Pool.Get() - defer conn.Close() - - var a contract.Addressable - err := getObjectById(conn, id, unmarshalObject, &a) - return a, err -} - -func (c *Client) GetAddressableByName(n string) (contract.Addressable, error) { - conn := c.Pool.Get() - defer conn.Close() - - var a contract.Addressable - err := getObjectByHash(conn, db.Addressable+":name", n, unmarshalObject, &a) - return a, err -} - -func (c *Client) GetAddressablesByTopic(t string) ([]contract.Addressable, error) { - return c.getAddressablesByValue(db.Addressable + ":topic:" + t) -} - -func (c *Client) GetAddressablesByPort(p int) ([]contract.Addressable, error) { - return c.getAddressablesByValue(db.Addressable + ":port:" + strconv.Itoa(p)) -} - -func (c *Client) GetAddressablesByPublisher(p string) ([]contract.Addressable, error) { - return c.getAddressablesByValue(db.Addressable + ":publisher:" + p) -} - -func (c *Client) GetAddressablesByAddress(add string) ([]contract.Addressable, error) { - return c.getAddressablesByValue(db.Addressable + ":address:" + add) -} - -func (c *Client) GetAddressables() ([]contract.Addressable, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.Addressable, 0, -1) - if err != nil { - return []contract.Addressable{}, err - } - - d := make([]contract.Addressable, len(objects)) - for i, object := range objects { - err = unmarshalObject(object, &d[i]) - if err != nil { - return []contract.Addressable{}, err - } - } - - return d, nil -} - -func (c *Client) DeleteAddressableById(id string) error { - conn := c.Pool.Get() - defer conn.Close() - - return deleteAddressable(conn, id) -} - -func (c *Client) getAddressablesByValue(v string) ([]contract.Addressable, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByValue(conn, v) - if err != nil { - return []contract.Addressable{}, err - } - - a := make([]contract.Addressable, len(objects)) - for i, object := range objects { - err = unmarshalObject(object, &a[i]) - if err != nil { - return []contract.Addressable{}, err - } - } - - return a, nil -} - -func addAddressable(conn redis.Conn, a contract.Addressable) (string, error) { - exists, err := redis.Bool(conn.Do("HEXISTS", db.Addressable+":name", a.Name)) - if err != nil { - return a.Id, err - } else if exists { - return a.Id, db.ErrNotUnique - } - - _, err = uuid.Parse(a.Id) - if err != nil { - a.Id = uuid.New().String() - } - id := a.Id - - ts := db.MakeTimestamp() - if a.Created == 0 { - a.Created = ts - } - a.Modified = ts - - m, err := marshalObject(a) - if err != nil { - return a.Id, err - } - - _ = conn.Send("MULTI") - _ = conn.Send("SET", id, m) - _ = conn.Send("ZADD", db.Addressable, 0, id) - _ = conn.Send("SADD", db.Addressable+":topic:"+a.Topic, id) - _ = conn.Send("SADD", db.Addressable+":port:"+strconv.Itoa(a.Port), id) - _ = conn.Send("SADD", db.Addressable+":publisher:"+a.Publisher, id) - _ = conn.Send("SADD", db.Addressable+":address:"+a.Address, id) - _ = conn.Send("HSET", db.Addressable+":name", a.Name, id) - _, err = conn.Do("EXEC") - if err != nil { - return a.Id, err - } - - return a.Id, err -} - -func deleteAddressable(conn redis.Conn, id string) error { - object, err := redis.Bytes(conn.Do("GET", id)) - if err == redis.ErrNil { - return db.ErrNotFound - } else if err != nil { - return err - } - - // TODO: ??? check if addressable is used by DeviceServices - - a := contract.Addressable{} - _ = unmarshalObject(object, &a) - - _ = conn.Send("MULTI") - _ = conn.Send("DEL", id) - _ = conn.Send("ZREM", db.Addressable, id) - _ = conn.Send("SREM", db.Addressable+":topic:"+a.Topic, id) - _ = conn.Send("SREM", db.Addressable+":port:"+strconv.Itoa(a.Port), id) - _ = conn.Send("SREM", db.Addressable+":publisher:"+a.Publisher, id) - _ = conn.Send("SREM", db.Addressable+":address:"+a.Address, id) - _ = conn.Send("HDEL", db.Addressable+":name", a.Name) - _, err = conn.Do("EXEC") - if err != nil { - return err - } - - return nil -} - -// /* ----------------------------- Device Service ----------------------------------*/ -func (c *Client) GetDeviceServiceByName(n string) (contract.DeviceService, error) { - conn := c.Pool.Get() - defer conn.Close() - - var d contract.DeviceService - err := getObjectByHash(conn, db.DeviceService+":name", n, unmarshalDeviceService, &d) - return d, err -} - -func (c *Client) GetDeviceServiceById(id string) (contract.DeviceService, error) { - conn := c.Pool.Get() - defer conn.Close() - - var d contract.DeviceService - err := getObjectById(conn, id, unmarshalDeviceService, &d) - return d, err -} - -func (c *Client) GetAllDeviceServices() ([]contract.DeviceService, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.DeviceService, 0, -1) - if err != nil { - return []contract.DeviceService{}, err - } - - d := make([]contract.DeviceService, len(objects)) - for i, object := range objects { - err = unmarshalDeviceService(object, &d[i]) - if err != nil { - return []contract.DeviceService{}, err - } - } - - return d, nil -} - -func (c *Client) GetDeviceServicesByAddressableId(id string) ([]contract.DeviceService, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByValue(conn, db.DeviceService+":addressable:"+id) - if err != nil { - return []contract.DeviceService{}, err - } - - d := make([]contract.DeviceService, len(objects)) - for i, object := range objects { - err = unmarshalDeviceService(object, &d[i]) - if err != nil { - return []contract.DeviceService{}, err - } - } - return d, nil -} - -func (c *Client) GetDeviceServicesWithLabel(l string) ([]contract.DeviceService, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByValue(conn, db.DeviceService+":label:"+l) - if err != nil { - return []contract.DeviceService{}, err - } - - d := make([]contract.DeviceService, len(objects)) - for i, object := range objects { - err = unmarshalDeviceService(object, &d[i]) - if err != nil { - return []contract.DeviceService{}, err - } - } - return d, nil -} - -func (c *Client) AddDeviceService(ds contract.DeviceService) (string, error) { - conn := c.Pool.Get() - defer conn.Close() - - return addDeviceService(conn, ds) -} - -func (c *Client) UpdateDeviceService(ds contract.DeviceService) error { - conn := c.Pool.Get() - defer conn.Close() - - err := deleteDeviceService(conn, ds.Id) - if err != nil { - return err - } - - ds.Modified = db.MakeTimestamp() - - _, err = addDeviceService(conn, ds) - - return err -} - -func (c *Client) DeleteDeviceServiceById(id string) error { - conn := c.Pool.Get() - defer conn.Close() - - return deleteDeviceService(conn, id) -} - -func addDeviceService(conn redis.Conn, ds contract.DeviceService) (string, error) { - aid := ds.Addressable.Id - _ = conn.Send("MULTI") - _ = conn.Send("HEXISTS", db.DeviceService+":name", ds.Name) - _ = conn.Send("EXISTS", aid) - _ = conn.Send("HGET", db.Addressable+":name", ds.Addressable.Name) - rep, err := redis.Values(conn.Do("EXEC")) - if err != nil { - return ds.Id, err - } - - // Verify uniquness of name - nex, err := redis.Bool(rep[0], nil) - if err != nil { - return "", err - } else if nex { - return "", db.ErrNotUnique - } - - // Verify existence of an addressable by ID or name - idex, err := redis.Bool(rep[1], nil) - if err != nil { - return ds.Id, err - } else if !idex { - aid, err = redis.String(rep[2], nil) - if err == redis.ErrNil { - return "", errors.New("Invalid addressable") - } else if err != nil { - return "", err - } - ds.Addressable.Id = aid // XXX This seems redundant - } - - _, err = uuid.Parse(ds.Id) - if err != nil { - ds.Id = uuid.New().String() - } - id := ds.Id - - ts := db.MakeTimestamp() - if ds.Created == 0 { - ds.Created = ts - } - ds.Modified = ts - - m, err := marshalDeviceService(ds) - if err != nil { - return "", err - } - - _ = conn.Send("MULTI") - _ = conn.Send("SET", id, m) - _ = conn.Send("ZADD", db.DeviceService, 0, id) - _ = conn.Send("HSET", db.DeviceService+":name", ds.Name, id) - _ = conn.Send("SADD", db.DeviceService+":addressable:"+aid, id) - for _, label := range ds.Labels { - _ = conn.Send("SADD", db.DeviceService+":label:"+label, id) - } - _, err = conn.Do("EXEC") - if err != nil { - return "", err - } - - return ds.Id, err -} - -func deleteDeviceService(conn redis.Conn, id string) error { - object, err := redis.Bytes(conn.Do("GET", id)) - if err == redis.ErrNil { - return db.ErrNotFound - } else if err != nil { - return err - } - - ds := contract.DeviceService{} - _ = unmarshalDeviceService(object, &ds) - - _ = conn.Send("MULTI") - _ = conn.Send("DEL", id) - _ = conn.Send("ZREM", db.DeviceService, id) - _ = conn.Send("HDEL", db.DeviceService+":name", ds.Name) - _ = conn.Send("SREM", db.DeviceService+":addressable:"+ds.Addressable.Id, id) - for _, label := range ds.Labels { - _ = conn.Send("SREM", db.DeviceService+":label:"+label, id) - } - _, err = conn.Do("EXEC") - return err -} - -// ----------------------Provision Watcher -----------------------------*/ -func (c *Client) GetAllProvisionWatchers() ([]contract.ProvisionWatcher, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.ProvisionWatcher, 0, -1) - if err != nil { - return []contract.ProvisionWatcher{}, err - } - - pw := make([]contract.ProvisionWatcher, len(objects)) - for i, object := range objects { - err = unmarshalProvisionWatcher(object, &pw[i]) - if err != nil { - return []contract.ProvisionWatcher{}, err - } - } - - return pw, nil -} - -func (c *Client) GetProvisionWatcherByName(n string) (contract.ProvisionWatcher, error) { - conn := c.Pool.Get() - defer conn.Close() - - var pw contract.ProvisionWatcher - err := getObjectByHash(conn, db.ProvisionWatcher+":name", n, unmarshalProvisionWatcher, &pw) - return pw, err -} - -func (c *Client) GetProvisionWatchersByIdentifier(k string, v string) (pw []contract.ProvisionWatcher, err error) { - return c.getProvisionWatchersByValue(db.ProvisionWatcher + ":identifier:" + k + ":" + v) -} - -func (c *Client) GetProvisionWatchersByServiceId(id string) ([]contract.ProvisionWatcher, error) { - return c.getProvisionWatchersByValue(db.ProvisionWatcher + ":service:" + id) -} - -func (c *Client) GetProvisionWatchersByProfileId(id string) ([]contract.ProvisionWatcher, error) { - return c.getProvisionWatchersByValue(db.ProvisionWatcher + ":profile:" + id) -} - -func (c *Client) GetProvisionWatcherById(id string) (contract.ProvisionWatcher, error) { - conn := c.Pool.Get() - defer conn.Close() - - var pw contract.ProvisionWatcher - err := getObjectById(conn, id, unmarshalProvisionWatcher, &pw) - return pw, err -} - -func (c *Client) getProvisionWatchersByValue(v string) ([]contract.ProvisionWatcher, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByValue(conn, v) - if err != nil { - return []contract.ProvisionWatcher{}, err - } - - pw := make([]contract.ProvisionWatcher, len(objects)) - for i, object := range objects { - err = unmarshalProvisionWatcher(object, &pw[i]) - if err != nil { - return []contract.ProvisionWatcher{}, err - } - } - - return pw, nil -} - -func (c *Client) AddProvisionWatcher(pw contract.ProvisionWatcher) (string, error) { - conn := c.Pool.Get() - defer conn.Close() - - return addProvisionWatcher(conn, pw) -} - -func (c *Client) UpdateProvisionWatcher(pw contract.ProvisionWatcher) error { - conn := c.Pool.Get() - defer conn.Close() - - err := deleteProvisionWatcher(conn, pw.Id) - if err != nil { - return err - } - - _, err = addProvisionWatcher(conn, pw) - return err -} - -func (c *Client) DeleteProvisionWatcherById(id string) error { - conn := c.Pool.Get() - defer conn.Close() - - return deleteProvisionWatcher(conn, id) -} - -func addProvisionWatcher(conn redis.Conn, pw contract.ProvisionWatcher) (string, error) { - pid := pw.Profile.Id - sid := pw.Service.Id - _ = conn.Send("MULTI") - _ = conn.Send("HEXISTS", db.ProvisionWatcher+":name", pw.Name) - _ = conn.Send("EXISTS", pid) - _ = conn.Send("HGET", db.DeviceProfile+":name", pw.Profile.Name) - _ = conn.Send("EXISTS", sid) - _ = conn.Send("HGET", db.DeviceService+":name", pw.Service.Name) - rep, err := redis.Values(conn.Do("EXEC")) - if err != nil { - return "", err - } - - // Verify uniquness of name - nex, err := redis.Bool(rep[0], nil) - if err != nil { - return "", err - } else if nex { - return "", db.ErrNotUnique - } - - // Verify existence of a device profile by ID or name - idex, err := redis.Bool(rep[1], nil) - if err != nil { - return "", err - } else if !idex { - pid, err = redis.String(rep[2], nil) - if err == redis.ErrNil { - return "", errors.New("Invalid Device Profile") - } else if err != nil { - return "", err - } - pw.Profile.Id = pid - } - - // Verify existence of a device profile by ID or name - idex, err = redis.Bool(rep[3], nil) - if err != nil { - return "", err - } else if !idex { - sid, err = redis.String(rep[4], nil) - if err == redis.ErrNil { - return "", errors.New("Invalid Device Service") - } else if err != nil { - return "", err - } - pw.Service.Id = sid - } - - _, err = uuid.Parse(pw.Id) - if err != nil { - pw.Id = uuid.New().String() - } - id := pw.Id - - ts := db.MakeTimestamp() - if pw.Created == 0 { - pw.Created = ts - } - pw.Modified = ts - - m, err := marshalProvisionWatcher(pw) - if err != nil { - return "", err - } - - _ = conn.Send("MULTI") - _ = conn.Send("SET", id, m) - _ = conn.Send("ZADD", db.ProvisionWatcher, 0, id) - _ = conn.Send("HSET", db.ProvisionWatcher+":name", pw.Name, id) - _ = conn.Send("SADD", db.ProvisionWatcher+":service:"+pw.Service.Id, id) - _ = conn.Send("SADD", db.ProvisionWatcher+":profile:"+pw.Profile.Id, id) - for k, v := range pw.Identifiers { - _ = conn.Send("SADD", db.ProvisionWatcher+":identifier:"+k+":"+v, id) - } - _, err = conn.Do("EXEC") - if err != nil { - return "", err - } - - return id, nil -} - -func deleteProvisionWatcher(conn redis.Conn, id string) error { - object, err := redis.Bytes(conn.Do("GET", id)) - if err == redis.ErrNil { - return db.ErrNotFound - } else if err != nil { - return err - } - - pw := contract.ProvisionWatcher{} - _ = unmarshalProvisionWatcher(object, &pw) - - _ = conn.Send("MULTI") - _ = conn.Send("DEL", id) - _ = conn.Send("ZREM", db.ProvisionWatcher, id) - _ = conn.Send("HDEL", db.ProvisionWatcher+":name", pw.Name) - _ = conn.Send("SREM", db.ProvisionWatcher+":service:"+pw.Service.Id, id) - _ = conn.Send("SREM", db.ProvisionWatcher+":profile:"+pw.Profile.Id, id) - for k, v := range pw.Identifiers { - _ = conn.Send("SREM", db.ProvisionWatcher+":identifier:"+k+":"+v, id) - } - _, err = conn.Do("EXEC") - return err -} - -// ------------------------Command -------------------------------------*/ -func (c *Client) GetAllCommands() ([]contract.Command, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.Command, 0, -1) - if err != nil { - return []contract.Command{}, err - } - - d := make([]contract.Command, len(objects)) - for i, object := range objects { - err = unmarshalObject(object, &d[i]) - if err != nil { - return []contract.Command{}, err - } - } - - return d, nil -} - -func (c *Client) GetCommandById(id string) (contract.Command, error) { - conn := c.Pool.Get() - defer conn.Close() - - var d contract.Command - err := getObjectById(conn, id, unmarshalObject, &d) - return d, err -} - -func (c *Client) GetCommandByNameAndDeviceId(cname string, did string) (contract.Command, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByValues(conn, db.Command+":name:"+cname, - db.Command+":device:"+did) - if err != nil { - return contract.Command{}, err - } - if len(objects) != 1 { - return contract.Command{}, db.ErrNotFound - } - - var cmd contract.Command - err = unmarshalObject(objects[0], &cmd) - if err != nil { - return contract.Command{}, err - } - - return cmd, nil -} - -func (c *Client) GetCommandsByName(n string) ([]contract.Command, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByValue(conn, db.Command+":name:"+n) - if err != nil { - return []contract.Command{}, err - } - - cmd := make([]contract.Command, len(objects)) - for i, object := range objects { - err = unmarshalObject(object, &cmd[i]) - if err != nil { - return []contract.Command{}, err - } - } - - return cmd, nil -} - -func addCommand(conn redis.Conn, tx bool, cmd contract.Command) (string, error) { - cmd.Id = uuid.New().String() - ts := db.MakeTimestamp() - if cmd.Created == 0 { - cmd.Created = ts - } - cmd.Modified = ts - - m, err := marshalObject(cmd) - if err != nil { - return "", err - } - - if tx { - _ = conn.Send("MULTI") - } - _ = conn.Send("SET", cmd.Id, m) - _ = conn.Send("ZADD", db.Command, 0, cmd.Id) - _ = conn.Send("SADD", db.Command+":name:"+cmd.Name, cmd.Id) - if tx { - _, err = conn.Do("EXEC") - if err != nil { - return "", err - } - } - - return cmd.Id, nil -} -func (c *Client) GetCommandsByDeviceId(did string) ([]contract.Command, error) { - conn := c.Pool.Get() - defer conn.Close() - - return getCommandsByDeviceId(conn, did) -} - -func getCommandsByDeviceId(conn redis.Conn, id string) (commands []contract.Command, err error) { - err = validateKeyExists(conn, id) - if err != nil { - if err == db.ErrNotFound { - err = types.NewErrItemNotFound(fmt.Sprintf("device with id %s not found", id)) - } - return []contract.Command{}, err - } - - objects, err := getObjectsByValue(conn, db.Command+":device:"+id) - if err != nil { - return []contract.Command{}, err - } - - commands = make([]contract.Command, len(objects)) - for i, object := range objects { - err = unmarshalObject(object, &commands[i]) - if err != nil { - return []contract.Command{}, err - } - } - return commands, nil -} - -// GetCommandsByDaviceName coming soon - -func deleteCommand(conn redis.Conn, cmd contract.Command) { - _ = conn.Send("DEL", cmd.Id) - _ = conn.Send("ZREM", db.Command, cmd.Id) - _ = conn.Send("SREM", db.Command+":name:"+cmd.Name, cmd.Id) -} - -func (c *Client) ScrubMetadata() (err error) { - conn := c.Pool.Get() - defer conn.Close() - - cols := []string{ - db.Addressable, db.Command, db.DeviceService, db.DeviceReport, db.DeviceProfile, - db.Device, db.ProvisionWatcher, - } - - for _, col := range cols { - err = unlinkCollection(conn, col) - if err != nil { - return err - } - } - - return nil -} diff --git a/internal/pkg/db/redis/models/db_command.go b/internal/pkg/db/redis/models/db_command.go deleted file mode 100644 index 64d7312e17..0000000000 --- a/internal/pkg/db/redis/models/db_command.go +++ /dev/null @@ -1,34 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 models - -type Adder interface { - Add() []DbCommand -} - -type Remover interface { - Remove() []DbCommand -} - -// DbCommand is used to represent information about a specific Redis command -// If this strategy works, this type is called "DbCommand" in order to differentiate -// from the EdgeX model Command -type DbCommand struct { - Command string //The actual Redis API command - Hash string //Indicates which hash index the command should be applied toward - Key string //Indicates the key that will be targeted in the given Hash - Value string //If applicable the value to be assigned to the key - Rank int64 //If applicable, the rank to be assigned to a given key -} diff --git a/internal/pkg/db/redis/models/interval.go b/internal/pkg/db/redis/models/interval.go deleted file mode 100644 index 461073049b..0000000000 --- a/internal/pkg/db/redis/models/interval.go +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 models - -import ( - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -const ( - IntervalKey = db.Interval - IntervalNameKey = db.Interval + ":name" -) - -var intervalKeys = []string{IntervalKey, IntervalNameKey} - -type Interval struct { - contract.Interval -} - -func NewInterval(from contract.Interval) (i Interval) { - i.Interval = from - return -} - -func (i Interval) Add() (cmds []DbCommand) { - cmds = make([]DbCommand, len(intervalKeys)) - for _, key := range intervalKeys { - switch key { - case IntervalKey: - cmds = append(cmds, DbCommand{Command: "ZADD", Hash: key, Key: i.ID, Rank: i.Timestamps.Modified}) - case IntervalNameKey: - cmds = append(cmds, DbCommand{Command: "HSET", Hash: key, Key: i.Name, Value: i.ID}) - } - } - return cmds -} - -func (i Interval) Remove() (cmds []DbCommand) { - cmds = make([]DbCommand, len(intervalKeys)) - for _, key := range intervalKeys { - switch key { - case IntervalKey: - cmds = append(cmds, DbCommand{Command: "ZREM", Hash: key, Key: i.ID}) - case IntervalNameKey: - cmds = append(cmds, DbCommand{Command: "HDEL", Key: i.Name}) - } - } - return cmds -} diff --git a/internal/pkg/db/redis/models/interval_action.go b/internal/pkg/db/redis/models/interval_action.go deleted file mode 100644 index 8729ab2d49..0000000000 --- a/internal/pkg/db/redis/models/interval_action.go +++ /dev/null @@ -1,72 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 models - -import ( - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -const ( - IntervalActionKey = db.IntervalAction - IntervalActionNameKey = db.IntervalAction + ":name" - IntervalActionParentKey = db.IntervalAction + ":parent" - IntervalActionTargetKey = db.IntervalAction + ":target" -) - -var intervalActionKeys = []string{IntervalActionKey, IntervalActionNameKey, IntervalActionParentKey, IntervalActionTargetKey} - -type IntervalAction struct { - contract.IntervalAction -} - -func NewIntervalAction(from contract.IntervalAction) (ia IntervalAction) { - ia.IntervalAction = from - return -} - -func (ia IntervalAction) Add() (cmds []DbCommand) { - cmds = make([]DbCommand, len(intervalActionKeys)) - for i, key := range intervalActionKeys { - switch key { - case IntervalActionKey: - cmds[i] = DbCommand{Command: "ZADD", Hash: key, Key: ia.ID, Rank: ia.Modified} - case IntervalActionNameKey: - cmds[i] = DbCommand{Command: "HSET", Hash: key, Key: ia.Name, Value: ia.ID} - case IntervalActionParentKey: - cmds[i] = DbCommand{Command: "SADD", Hash: key + ":" + ia.Interval, Key: ia.ID} - case IntervalActionTargetKey: - cmds[i] = DbCommand{Command: "SADD", Hash: key + ":" + ia.Target, Key: ia.ID} - } - } - return cmds -} - -func (ia IntervalAction) Remove() (cmds []DbCommand) { - cmds = make([]DbCommand, len(intervalActionKeys)) - for i, key := range intervalActionKeys { - switch key { - case IntervalActionKey: - cmds[i] = DbCommand{Command: "ZREM", Hash: key, Key: ia.ID} - case IntervalActionNameKey: - cmds[i] = DbCommand{Command: "HDEL", Hash: key, Key: ia.Name} - case IntervalActionParentKey: - cmds[i] = DbCommand{Command: "SREM", Hash: key + ":" + ia.Interval, Key: ia.ID} - case IntervalActionTargetKey: - cmds[i] = DbCommand{Command: "SREM", Hash: key + ":" + ia.Target, Key: ia.ID} - } - } - return cmds -} diff --git a/internal/pkg/db/redis/notifications.go b/internal/pkg/db/redis/notifications.go deleted file mode 100644 index a63b10ac16..0000000000 --- a/internal/pkg/db/redis/notifications.go +++ /dev/null @@ -1,959 +0,0 @@ -/******************************************************************************* - * Copyright (C) 2018 IOTech Ltd - * - * 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 redis - -import ( - "fmt" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/gomodule/redigo/redis" - "github.com/google/uuid" - "github.com/pkg/errors" -) - -// ******************************* NOTIFICATIONS ********************************** -func (c Client) AddNotification(n contract.Notification) (string, error) { - conn := c.Pool.Get() - defer conn.Close() - - err := addNotification(conn, &n) - if err != nil { - return "", err - } - return n.ID, nil -} - -func (c Client) UpdateNotification(n contract.Notification) error { - conn := c.Pool.Get() - defer conn.Close() - - err := deleteNotification(conn, n.ID) - if err != nil { - return err - } - n.Modified = db.MakeTimestamp() - return addNotification(conn, &n) -} - -// Get all notifications -func (c Client) GetNotifications() (n []contract.Notification, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.Notification, 0, -1) - if err != nil { - return nil, err - } - - n, err = unmarshalNotifications(objects) - if err != nil { - return n, err - } - - return n, nil -} - -func (c Client) GetNotificationById(id string) (notification contract.Notification, err error) { - conn := c.Pool.Get() - defer conn.Close() - - err = getObjectById(conn, id, unmarshalObject, ¬ification) - if err != nil { - return contract.Notification{}, err - } - return notification, nil -} - -func (c Client) GetNotificationBySlug(slug string) (notification contract.Notification, err error) { - conn := c.Pool.Get() - defer conn.Close() - - notification, err = notificationBySlug(conn, slug) - if err != nil { - if err == redis.ErrNil { - return notification, db.ErrNotFound - } - return notification, err - } - - return notification, nil -} - -func (c Client) GetNotificationBySender(sender string, limit int) ([]contract.Notification, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.Notification+":sender:"+sender, 0, limit-1) - if err != nil { - return nil, err - } - - notifications, err := unmarshalNotifications(objects) - if err != nil { - return nil, err - } - - return notifications, nil -} - -func (c Client) GetNotificationsByLabels(labels []string, limit int) (notifications []contract.Notification, err error) { - conn := c.Pool.Get() - defer conn.Close() - - for _, label := range labels { - var objects, err = getObjectsByRange(conn, db.Notification+":label:"+label, 0, limit-1) - if err != nil { - if err != redis.ErrNil { - return notifications, err - } - } - - t, err := unmarshalNotifications(objects) - if err != nil { - return nil, err - } - - notifications = append(notifications, t...) - - limit -= len(objects) - if limit < 0 { - break - } - } - return notifications, nil -} - -func (c Client) GetNotificationsByStartEnd(start int64, end int64, limit int) ([]contract.Notification, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByScore(conn, db.Notification+":created", start, end, limit) - if err != nil { - return nil, err - } - notifications, err := unmarshalNotifications(objects) - if err != nil { - return nil, err - } - - return notifications, nil -} - -func (c Client) GetNotificationsByStart(start int64, limit int) ([]contract.Notification, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByScore(conn, db.Notification+":created", start, -1, limit) - if err != nil { - return nil, err - } - notifications, err := unmarshalNotifications(objects) - if err != nil { - return nil, err - } - - return notifications, nil -} - -func (c Client) GetNotificationsByEnd(end int64, limit int) ([]contract.Notification, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByScore(conn, db.Notification+":created", 0, end, limit) - if err != nil { - return nil, err - } - notifications, err := unmarshalNotifications(objects) - if err != nil { - return nil, err - } - - return notifications, nil -} - -func (c Client) GetNewNotifications(limit int) ([]contract.Notification, error) { - conn := c.Pool.Get() - defer conn.Close() - - //REF: using NEW, based on Mongo impln - objects, err := getObjectsByScore(conn, db.Notification+":status:"+contract.New, 0, -1, limit) - if err != nil { - return nil, err - } - - notifications, err := unmarshalNotifications(objects) - if err != nil { - return nil, err - } - - return notifications, nil -} - -func (c Client) GetNewNormalNotifications(limit int) ([]contract.Notification, error) { - conn := c.Pool.Get() - defer conn.Close() - - //option 2: sorted set of status+severity should be created and get objects based on - objects, err := getObjectsByValuesSorted(conn, limit, db.Notification+":status:"+contract.New, db.Notification+":severity:"+contract.Normal) - if err != nil { - return nil, err - } - notifications, err := unmarshalNotifications(objects) - if err != nil { - return nil, err - } - - return notifications, nil - -} - -func (c Client) MarkNotificationProcessed(n contract.Notification) error { - conn := c.Pool.Get() - defer conn.Close() - - n.Status = contract.NotificationsStatus(contract.Processed) - return c.UpdateNotification(n) -} - -func (c Client) DeleteNotificationById(id string) error { - conn := c.Pool.Get() - defer conn.Close() - - n, err := c.GetNotificationById(id) - if err != nil { - return err - } - - err = c.deleteTransmissionBySlug(conn, n.Slug) - if err != nil { - return err - } - - return deleteNotification(conn, id) -} - -func (c Client) DeleteNotificationBySlug(slug string) error { - conn := c.Pool.Get() - defer conn.Close() - - n, err := notificationBySlug(conn, slug) - if err != nil { - return err - } - - err = c.deleteTransmissionBySlug(conn, n.Slug) - if err != nil { - return err - } - - return deleteNotification(conn, n.ID) -} - -// DeleteNotificationsOld remove all the notifications that are older than the given age -func (c Client) DeleteNotificationsOld(age int) error { - conn := c.Pool.Get() - defer conn.Close() - - currentTime := db.MakeTimestamp() - end := int64(int(currentTime) - age) - - objects, err := getObjectsByScore(conn, db.Notification+":modified", 0, end, 0) - - for _, object := range objects { - if len(object) > 0 { - var n contract.Notification - err := unmarshalObject(object, &n) - if err != nil { - return err - } - // Delete processed notification - if n.Status != contract.Processed { - continue - } - err = deleteNotification(conn, n.ID) - if err != nil { - return err - } - } - } - return err -} - -// ******************************* SUBSCRIPTIONS ********************************** -func (c Client) AddSubscription(s contract.Subscription) (string, error) { - conn := c.Pool.Get() - defer conn.Close() - - err := addSubscription(conn, &s) - if err != nil { - return "", err - } - - return s.ID, nil -} - -func (c Client) UpdateSubscription(s contract.Subscription) error { - conn := c.Pool.Get() - defer conn.Close() - - // update modified, delete and add subscription - err := deleteSubscription(conn, s.ID) - if err != nil { - return nil - } - - s.Modified = db.MakeTimestamp() - return addSubscription(conn, &s) -} - -func (c Client) GetSubscriptions() ([]contract.Subscription, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.Subscription, 0, -1) - if err != nil { - return nil, err - } - - s, err := unmarshalSubscriptions(objects) - if err != nil { - return s, err - } - - return s, nil -} - -func (c Client) GetSubscriptionById(id string) (s contract.Subscription, err error) { - conn := c.Pool.Get() - defer conn.Close() - - err = getObjectById(conn, id, unmarshalObject, &s) - if err != nil { - return s, err - } - - return s, nil -} - -func (c *Client) DeleteSubscriptionById(id string) error { - conn := c.Pool.Get() - defer conn.Close() - - return deleteSubscription(conn, id) -} - -func (c Client) GetSubscriptionBySlug(slug string) (s contract.Subscription, err error) { - conn := c.Pool.Get() - defer conn.Close() - - s, err = subscriptionBySlug(conn, slug) - if err != nil { - return s, err - } - return s, nil -} - -func (c Client) GetSubscriptionByReceiver(receiver string) ([]contract.Subscription, error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.Subscription+":receiver:"+receiver, 0, -1) - if err != nil { - return nil, err - } - - s, err := unmarshalSubscriptions(objects) - if err != nil { - return s, err - } - - return s, nil -} - -func (c Client) GetSubscriptionByCategories(categories []string) (s []contract.Subscription, err error) { - conn := c.Pool.Get() - defer conn.Close() - - args := make([]string, len(categories)) - for i, c := range categories { - args[i] = db.Subscription + ":category:" + c - } - - objects, err := getUnionObjectsByValues(conn, args...) - if err != nil { - return s, err - } - - s, err = unmarshalSubscriptions(objects) - if err != nil { - return s, err - } - - return s, nil -} - -func (c Client) GetSubscriptionByLabels(labels []string) (s []contract.Subscription, err error) { - conn := c.Pool.Get() - defer conn.Close() - - args := make([]string, len(labels)) - for i, label := range labels { - args[i] = db.Subscription + ":label:" + label - } - - objects, err := getUnionObjectsByValues(conn, args...) - if err != nil { - return s, err - } - - s, err = unmarshalSubscriptions(objects) - if err != nil { - return s, err - } - - return s, nil -} - -func (c Client) GetSubscriptionByCategoriesLabels(categories []string, labels []string) (s []contract.Subscription, err error) { - conn := c.Pool.Get() - defer conn.Close() - - var args []string - for _, c := range categories { - args = append(args, db.Subscription+":category:"+c) - } - for _, label := range labels { - args = append(args, db.Subscription+":label:"+label) - } - - objects, err := getUnionObjectsByValues(conn, args...) - if err != nil { - return s, err - } - - s, err = unmarshalSubscriptions(objects) - if err != nil { - return s, err - } - - return s, nil -} - -func (c Client) DeleteSubscriptionBySlug(slug string) error { - conn := c.Pool.Get() - defer conn.Close() - - s, err := subscriptionBySlug(conn, slug) - if err != nil { - return err - } - - return deleteSubscription(conn, s.ID) - -} - -// ******************************* TRANSMISSIONS ********************************** -func (c Client) AddTransmission(t contract.Transmission) (string, error) { - conn := c.Pool.Get() - defer conn.Close() - - err := addTransmission(conn, &t) - if err != nil { - return "", err - } - - return t.ID, nil -} - -func (c Client) UpdateTransmission(t contract.Transmission) error { - conn := c.Pool.Get() - defer conn.Close() - - err := deleteTransmission(conn, t.ID) - if err != nil { - return nil - } - - t.Modified = db.MakeTimestamp() - return addTransmission(conn, &t) -} - -func (c Client) GetTransmissionsByNotificationSlug(slug string, limit int) (transmissions []contract.Transmission, err error) { - conn := c.Pool.Get() - defer conn.Close() - - return getTransmissionsByNotificationSlugAndStartEnd(conn, slug, 0, -1, limit) -} - -func (c Client) GetTransmissionsByNotificationSlugAndStartEnd(slug string, start int64, end int64, limit int) (transmissions []contract.Transmission, err error) { - conn := c.Pool.Get() - defer conn.Close() - - return getTransmissionsByNotificationSlugAndStartEnd(conn, slug, start, end, limit) -} - -func (c Client) GetTransmissionsByStartEnd(start int64, end int64, limit int) (transmissions []contract.Transmission, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByScore(conn, db.Transmission+":created", start, end, limit) - if err != nil { - return transmissions, err - } - - transmissions, err = unmarshalTransmissions(objects) - if err != nil { - return transmissions, err - } - - return transmissions, nil -} - -func (c Client) GetTransmissionsByStart(start int64, limit int) (transmissions []contract.Transmission, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByScore(conn, db.Transmission+":created", start, -1, limit) - if err != nil { - return transmissions, err - } - - transmissions, err = unmarshalTransmissions(objects) - if err != nil { - return transmissions, err - } - - return transmissions, nil -} - -func (c Client) GetTransmissionById(id string) (transmission contract.Transmission, err error) { - conn := c.Pool.Get() - defer conn.Close() - - err = getObjectById(conn, id, unmarshalObject, &transmission) - if err != nil { - return transmission, err - } - return transmission, nil -} - -func (c Client) GetTransmissionsByEnd(end int64, limit int) (transmissions []contract.Transmission, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByScore(conn, db.Transmission+":created", -1, end, limit) - if err != nil { - return transmissions, err - } - - transmissions, err = unmarshalTransmissions(objects) - if err != nil { - return transmissions, err - } - - return transmissions, nil -} - -func (c Client) GetTransmissionsByStatus(limit int, status contract.TransmissionStatus) (transmissions []contract.Transmission, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, db.Transmission+":status:"+string(status), 0, limit-1) - if err != nil { - return transmissions, err - } - - transmissions, err = unmarshalTransmissions(objects) - if err != nil { - return transmissions, err - } - - return transmissions, nil -} - -// DeleteTransmission delete old transmission with specified status -func (c Client) DeleteTransmission(age int64, status contract.TransmissionStatus) error { - conn := c.Pool.Get() - defer conn.Close() - - currentTime := db.MakeTimestamp() - end := currentTime - age - - objects, err := getObjectsByRangeFilter(conn, db.Transmission+":modified", db.Transmission+":status:"+fmt.Sprintf("%s", status), 0, int(end)) - if err != nil { - return err - } - transmissions, err := unmarshalTransmissions(objects) - if err != nil { - return err - } - - for _, transmission := range transmissions { - err = deleteTransmission(conn, transmission.ID) - if err != nil { - return err - } - } - return err -} - -// Cleanup delete all notifications and associated transmissions -func (c Client) Cleanup() error { - //conn := c.Pool.Get() - //defer conn.Close() - // - //cols := []string{ - // db.Transmission, db.Notification, - //} - // - //for _, col := range cols { - // err := unlinkCollection(conn, col) - // if err != nil { - // return err - // } - //} - err := c.CleanupOld(0) - if err != nil { - return err - } - return nil -} - -// Cleanup delete old notifications and associated transmissions -func (c Client) CleanupOld(age int) error { - conn := c.Pool.Get() - defer conn.Close() - - currentTime := db.MakeTimestamp() - end := currentTime - int64(age) - objects, err := getObjectsByScore(conn, db.Notification+":created", 0, end, -1) - if err != nil { - return err - } - - notifications, err := unmarshalNotifications(objects) - if err != nil { - return err - } - - for _, notification := range notifications { - err = c.DeleteNotificationById(notification.ID) - if err != nil { - return err - } - transmissions, err := c.GetTransmissionsByNotificationSlug(notification.Slug, -1) - if err != nil { - return err - } - for _, transmission := range transmissions { - err = deleteTransmission(conn, transmission.ID) - if err != nil { - return err - } - } - } - - return nil -} - -// ************************** HELPER FUNCTIONS *************************** -func addNotification(conn redis.Conn, n *contract.Notification) error { - exist, err := redis.Bool(conn.Do("HEXISTS", db.Notification+":slug", n.Slug)) - if err != nil { - return err - } else if exist { - return errors.Errorf("%v, slug=%v", db.ErrNotUnique, n.Slug) - } - - if n.Created == 0 { - n.Created = db.MakeTimestamp() - n.Modified = n.Created - } - - if n.ID == "" { - n.ID = uuid.New().String() - } - - m, err := marshalObject(n) - if err != nil { - return err - } - id := n.ID - - _ = conn.Send("MULTI") - _ = conn.Send("SET", id, m) - _ = conn.Send("ZADD", db.Notification, 0, id) - _ = conn.Send("HSET", db.Notification+":slug", n.Slug, id) - _ = conn.Send("ZADD", db.Notification+":sender:"+n.Sender, 0, id) - _ = conn.Send("ZADD", db.Notification+":status:"+n.Status, 0, id) - _ = conn.Send("ZADD", db.Notification+":severity:"+n.Severity, 0, id) - _ = conn.Send("ZADD", db.Notification+":created", n.Created, id) - _ = conn.Send("ZADD", db.Notification+":modified", n.Modified, id) //sorted set based on age - for _, label := range n.Labels { - _ = conn.Send("ZADD", db.Notification+":label:"+label, 0, id) - } - _, err = conn.Do("EXEC") - if err != nil { - return err - } - - return nil -} - -func deleteNotification(conn redis.Conn, id string) error { - var n contract.Notification - err := getObjectById(conn, id, unmarshalObject, &n) - if err != nil { - return err - } - - _ = conn.Send("MULTI") - _ = conn.Send("DEL", id) - _ = conn.Send("ZREM", db.Notification, id) - _ = conn.Send("HDEL", db.Notification+":slug", n.Slug) - _ = conn.Send("ZREM", db.Notification+":sender:"+n.Sender, id) - _ = conn.Send("ZREM", db.Notification+":status:"+n.Status, id) - _ = conn.Send("ZREM", db.Notification+":severity:"+n.Severity, id) - _ = conn.Send("ZREM", db.Notification+":created", id) - _ = conn.Send("ZREM", db.Notification+":modified", id) - for _, label := range n.Labels { - _ = conn.Send("ZREM", db.Notification+":label:"+label, id) - } - _, err = conn.Do("EXEC") - - return err - -} - -func addSubscription(conn redis.Conn, s *contract.Subscription) error { - exist, err := redis.Bool(conn.Do("HEXISTS", db.Subscription+":slug", s.Slug)) - if err != nil { - return err - } else if exist { - return errors.Errorf("%v, slug=%v", db.ErrNotUnique, s.Slug) - } - - if s.Created == 0 { - s.Created = db.MakeTimestamp() - s.Modified = s.Created - } - - if s.ID == "" { - s.ID = uuid.New().String() - } - - m, err := marshalObject(s) - if err != nil { - return err - } - - id := s.ID - - _ = conn.Send("MULTI") - _ = conn.Send("SET", id, m) - _ = conn.Send("ZADD", db.Subscription, 0, id) - _ = conn.Send("HSET", db.Subscription+":slug", s.Slug, id) - _ = conn.Send("ZADD", db.Subscription+":receiver:"+s.Receiver, 0, id) - for _, label := range s.SubscribedLabels { - _ = conn.Send("SADD", db.Subscription+":label:"+label, id) - } - for _, category := range s.SubscribedCategories { - _ = conn.Send("SADD", db.Subscription+":category:"+category, id) - } - _, err = conn.Do("EXEC") - - return err -} - -func deleteSubscription(conn redis.Conn, id string) error { - var s contract.Subscription - err := getObjectById(conn, id, unmarshalObject, &s) - if err != nil { - return err - } - - _ = conn.Send("MULTI") - _ = conn.Send("DEL", id) - _ = conn.Send("ZREM", db.Subscription, id) - _ = conn.Send("HDEL", db.Subscription+":slug", s.Slug) - _ = conn.Send("ZREM", db.Subscription+":receiver:"+s.Receiver, id) - for _, label := range s.SubscribedLabels { - _ = conn.Send("SREM", db.Subscription+":label:"+label, id) - } - for _, category := range s.SubscribedCategories { - _ = conn.Send("SREM", db.Subscription+":category:"+category, id) - } - _, err = conn.Do("EXEC") - - return err -} - -func addTransmission(conn redis.Conn, t *contract.Transmission) error { - if t.Created == 0 { - t.Created = db.MakeTimestamp() - t.Modified = t.Created - } - - if t.ID == "" { - t.ID = uuid.New().String() - } - - m, err := marshalObject(t) - if err != nil { - return err - } - id := t.ID - - _ = conn.Send("MULTI") - _ = conn.Send("SET", id, m) - _ = conn.Send("ZADD", db.Transmission, 0, id) - _ = conn.Send("ZADD", db.Transmission+":slug:"+t.Notification.Slug, t.Created, id) - _ = conn.Send("ZADD", db.Transmission+":status:"+t.Status, t.ResendCount, id) - _ = conn.Send("ZADD", db.Transmission+":resendcount", t.ResendCount, id) - _ = conn.Send("ZADD", db.Transmission+":created", t.Created, id) - _ = conn.Send("ZADD", db.Transmission+":modified", t.Modified, id) - _, err = conn.Do("EXEC") - if err != nil { - return err - } - - return nil -} - -func deleteTransmission(conn redis.Conn, id string) error { - var t contract.Transmission - err := getObjectById(conn, id, unmarshalObject, &t) - if err != nil { - return err - } - - _ = conn.Send("MULTI") - _ = conn.Send("DEL", id) - _ = conn.Send("ZREM", db.Transmission, id) - _ = conn.Send("ZREM", db.Transmission+":slug"+t.Notification.Slug, t.Notification.Slug) - _ = conn.Send("ZREM", db.Transmission+":status:"+t.Status, id) - _ = conn.Send("ZREM", db.Transmission+":resendcount", id) - _ = conn.Send("ZREM", db.Transmission+":created", id) - _ = conn.Send("ZREM", db.Transmission+":modified", id) - _, err = conn.Do("EXEC") - - return err -} - -func (c Client) deleteTransmissionBySlug(conn redis.Conn, slug string) error { - transmissions, err := c.GetTransmissionsByNotificationSlug(slug, -1) - if err != nil { - return err - } - for _, transmission := range transmissions { - err = deleteTransmission(conn, transmission.ID) - if err != nil { - return err - } - } - return nil -} - -func getTransmissionsByNotificationSlugAndStartEnd(conn redis.Conn, slug string, start int64, end int64, limit int) (transmissions []contract.Transmission, err error) { - objects, err := getObjectsByScore(conn, db.Transmission+":slug:"+slug, start, end, limit) - if err != nil { - return transmissions, err - } - - transmissions, err = unmarshalTransmissions(objects) - if err != nil { - return transmissions, err - } - - return transmissions, nil -} - -func unmarshalNotifications(objects [][]byte) ([]contract.Notification, error) { - var unmarshalObjects []contract.Notification - for _, o := range objects { - if len(o) > 0 { - var m contract.Notification - err := unmarshalObject(o, &m) - if err != nil { - return unmarshalObjects, err - } - unmarshalObjects = append(unmarshalObjects, m) - } - } - return unmarshalObjects, nil -} - -func unmarshalSubscriptions(objects [][]byte) ([]contract.Subscription, error) { - var unmarshalObjects []contract.Subscription - for _, o := range objects { - if len(o) > 0 { - var m contract.Subscription - err := unmarshalObject(o, &m) - if err != nil { - return unmarshalObjects, err - } - unmarshalObjects = append(unmarshalObjects, m) - } - } - return unmarshalObjects, nil -} - -func unmarshalTransmissions(objects [][]byte) ([]contract.Transmission, error) { - var unmarshalObjects []contract.Transmission - for _, o := range objects { - if len(o) > 0 { - var m contract.Transmission - err := unmarshalObject(o, &m) - if err != nil { - return unmarshalObjects, err - } - unmarshalObjects = append(unmarshalObjects, m) - } - } - return unmarshalObjects, nil -} - -func notificationBySlug(conn redis.Conn, slug string) (notification contract.Notification, err error) { - id, err := redis.String(conn.Do("HGET", db.Notification+":slug", slug)) - if err != nil { - if err == redis.ErrNil { - return notification, db.ErrNotFound - } - return notification, err - } - - err = getObjectById(conn, id, unmarshalObject, ¬ification) - if err != nil { - return notification, err - } - - return notification, nil -} - -func subscriptionBySlug(conn redis.Conn, slug string) (subscription contract.Subscription, err error) { - id, err := redis.String(conn.Do("HGET", db.Subscription+":slug", slug)) - if err != nil { - if err == redis.ErrNil { - return subscription, db.ErrNotFound - } - return subscription, err - } - - err = getObjectById(conn, id, unmarshalObject, &subscription) - if err != nil { - return subscription, err - } - - return subscription, nil -} diff --git a/internal/pkg/db/redis/object.go b/internal/pkg/db/redis/object.go deleted file mode 100644 index 83dcfe4e77..0000000000 --- a/internal/pkg/db/redis/object.go +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Redis Labs Inc. - * - * 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 redis - -import ( - "encoding/json" -) - -type marshalFunc func(in interface{}) (out []byte, err error) -type unmarshalFunc func(in []byte, out interface{}) (err error) - -func marshalObject(in interface{}) (out []byte, err error) { - return json.Marshal(in) -} - -func unmarshalObject(in []byte, out interface{}) (err error) { - return json.Unmarshal(in, out) -} diff --git a/internal/pkg/db/redis/provision_watcher.go b/internal/pkg/db/redis/provision_watcher.go deleted file mode 100644 index af4a5f1531..0000000000 --- a/internal/pkg/db/redis/provision_watcher.go +++ /dev/null @@ -1,88 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Redis Labs Inc. - * - * 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 redis - -import ( - "fmt" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type redisProvisionWatcher struct { - contract.Timestamps - Id string - Name string - Identifiers map[string]string - BlockingIdentifiers map[string][]string - Profile string - Service string - OperatingState contract.OperatingState - AdminState contract.AdminState -} - -func marshalProvisionWatcher(pw contract.ProvisionWatcher) (out []byte, err error) { - s := redisProvisionWatcher{ - Timestamps: pw.Timestamps, - Id: pw.Id, - Name: pw.Name, - Identifiers: pw.Identifiers, - BlockingIdentifiers: pw.BlockingIdentifiers, - Profile: pw.Profile.Id, - Service: pw.Service.Id, - OperatingState: pw.OperatingState, - AdminState: pw.AdminState, - } - - return marshalObject(s) -} - -func unmarshalProvisionWatcher(o []byte, pw interface{}) (err error) { - var s redisProvisionWatcher - - err = unmarshalObject(o, &s) - if err != nil { - return err - } - - switch x := pw.(type) { - case *contract.ProvisionWatcher: - x.Timestamps = s.Timestamps - x.Id = s.Id - x.Name = s.Name - x.Identifiers = s.Identifiers - x.BlockingIdentifiers = s.BlockingIdentifiers - x.OperatingState = s.OperatingState - x.AdminState = s.AdminState - - conn, err := getConnection() - if err != nil { - return err - } - defer conn.Close() - - err = getObjectById(conn, s.Profile, unmarshalDeviceProfile, &x.Profile) - if err != nil { - return err - } - - err = getObjectById(conn, s.Service, unmarshalDeviceService, &x.Service) - if err != nil { - return err - } - - return nil - default: - return fmt.Errorf("Can only unmarshal into a *ProvisionWatcher, got %T", x) - } -} diff --git a/internal/pkg/db/redis/queries.go b/internal/pkg/db/redis/queries.go deleted file mode 100644 index 45014483ff..0000000000 --- a/internal/pkg/db/redis/queries.go +++ /dev/null @@ -1,328 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Redis Labs Inc. - * - * 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 redis - -import ( - "encoding/json" - "strconv" - - "github.com/gomodule/redigo/redis" - "github.com/google/uuid" - - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/pkg/db/redis/models" -) - -func getObjectById(conn redis.Conn, id string, unmarshal unmarshalFunc, out interface{}) error { - object, err := redis.Bytes(conn.Do("GET", id)) - if err == redis.ErrNil { - return db.ErrNotFound - } else if err != nil { - return err - } - - return unmarshal(object, out) -} - -// TODO: Discuss this with Andre as a possibly replacement for getObjectByHash -// 1.) key/value seems clearer to me than hash/field for equivalent concepts. However the latter -// may be more consistently used in the Redis community. If so, revert. -// 2.) Not sure the custom "unmarshal" function is necessary when no domain logic is encapsulated -// within the Redis-based models. If the signatures of the Redis models are the same as contract -// then just use contract. However we have the capability to specialize the Redis models as -// needed now should a future requirement arise. -func getObjectByKey(conn redis.Conn, key string, value string, out interface{}) error { - id, err := redis.String(conn.Do("HGET", key, value)) - if err == redis.ErrNil { - return db.ErrNotFound - } else if err != nil { - return err - } - - object, err := redis.Bytes(conn.Do("GET", id)) - if err != nil { - return err - } - return json.Unmarshal(object, out) -} - -func getObjectByHash(conn redis.Conn, hash string, field string, unmarshal unmarshalFunc, out interface{}) error { - id, err := redis.String(conn.Do("HGET", hash, field)) - if err == redis.ErrNil { - return db.ErrNotFound - } else if err != nil { - return err - } - - object, err := redis.Bytes(conn.Do("GET", id)) - if err != nil { - return err - } - - return unmarshal(object, out) -} - -func getObjectsByValue(conn redis.Conn, v string) ([][]byte, error) { - ids, err := redis.Values(conn.Do("SMEMBERS", v)) - if err != nil { - return nil, err - } - - if len(ids) == 0 { - return nil, nil - } - - objects, err := redis.ByteSlices(conn.Do("MGET", ids...)) - if err != nil { - return nil, err - } - - return objects, nil -} - -func getObjectsByValues(conn redis.Conn, vals ...string) ([][]byte, error) { - args := redis.Args{} - for _, v := range vals { - args = args.Add(v) - } - ids, err := redis.Values(conn.Do("SINTER", args...)) - if err != nil { - return nil, err - } - - if len(ids) == 0 { - return nil, nil - } - - objects, err := redis.ByteSlices(conn.Do("MGET", ids...)) - if err != nil { - return nil, err - } - - return objects, nil -} - -// getObjectsByRange retrieves the entries for keys enumerated in a sorted set. -// The entries are retrieved in the sorted set order. -func getObjectsByRange(conn redis.Conn, key string, start, end int) ([][]byte, error) { - return getObjectsBySomeRange(conn, "ZRANGE", key, start, end) -} - -// getObjectsByRevRange retrieves the entries for keys enumerated in a sorted set. -// The entries are retrieved in the reverse sorted set order. -func getObjectsByRevRange(conn redis.Conn, key string, start int, end int) ([][]byte, error) { - return getObjectsBySomeRange(conn, "ZREVRANGE", key, start, end) -} - -// getObjectsBySomeRange retrieves the entries for keys enumerated in a sorted set using the specified Redis range -// command (i.e. RANGE, REVRANGE). The entries are retrieved in the order specified by the supplied Redis command. -func getObjectsBySomeRange(conn redis.Conn, command string, key string, start int, end int) ([][]byte, error) { - ids, err := redis.Values(conn.Do(command, key, start, end)) - if err != nil && err != redis.ErrNil { - return nil, err - } - - var result [][]byte - if len(ids) > 0 { - result, err = redis.ByteSlices(conn.Do("MGET", ids...)) - if err != nil { - return nil, err - } - } - - var objects [][]byte - for _, obj := range result { - if obj != nil { - objects = append(objects, obj) - } - } - - return objects, nil - -} - -// Return objects by a score from a zset -// if limit is 0, all are returned -// if end is negative, it is considered as positive infinity -func getObjectsByRangeFilter(conn redis.Conn, key string, filter string, start, end int) ([][]byte, error) { - ids, err := redis.Values(conn.Do("ZRANGE", key, start, end)) - if err != nil && err != redis.ErrNil { - return nil, err - } - - var objects [][]byte - // https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating - fids := ids[:0] - if len(ids) > 0 { - for _, id := range ids { - err := conn.Send("ZSCORE", filter, id) - if err != nil { - return nil, err - } - } - scores, err := redis.Strings(conn.Do("")) - if err != nil { - return nil, err - } - - for i, score := range scores { - if score != "" { - fids = append(fids, ids[i]) - } - } - - objects, err = redis.ByteSlices(conn.Do("MGET", fids...)) - if err != nil { - return nil, err - } - } - return objects, nil -} - -func getObjectsByScore(conn redis.Conn, key string, start, end int64, limit int) ([][]byte, error) { - args := []interface{}{key, start} - if end < 0 { - args = append(args, "+inf") - } else { - args = append(args, end) - } - if limit != 0 { - args = append(args, "LIMIT") - args = append(args, 0) - args = append(args, limit) - } - ids, err := redis.Values(conn.Do("ZRANGEBYSCORE", args...)) - if err != nil && err != redis.ErrNil { - return nil, err - } - - var objects [][]byte - if len(ids) > 0 { - objects, err = redis.ByteSlices(conn.Do("MGET", ids...)) - if err != nil { - return nil, err - } - } - return objects, nil -} - -// addObject is responsible for setting the object's primary record and then sending the appropriate -// follow-on commands as provided by the caller. - -// Transactions are managed outside of this function. -func addObject(data []byte, adder models.Adder, id string, conn redis.Conn) { - _ = conn.Send("SET", id, data) - - for _, cmd := range adder.Add() { - switch cmd.Command { - case "ZADD": - _ = conn.Send(cmd.Command, cmd.Hash, cmd.Rank, cmd.Key) - case "SADD": - _ = conn.Send(cmd.Command, cmd.Hash, cmd.Key) - case "HSET": - _ = conn.Send(cmd.Command, cmd.Hash, cmd.Key, cmd.Value) - } - } -} - -// deleteObject is responsible for removing the object's primary record and then sending the appropriate -// follow-on commands as provided by the caller. -// -// Transactions are managed outside of this function. -func deleteObject(remover models.Remover, id string, conn redis.Conn) { - _ = conn.Send("DEL", id) - - for _, cmd := range remover.Remove() { - switch cmd.Command { - case "ZREM": - fallthrough - case "SREM": - fallthrough - case "HDEL": - _ = conn.Send(cmd.Command, cmd.Hash, cmd.Key) - } - } -} - -func getUnionObjectsByValues(conn redis.Conn, vals ...string) ([][]byte, error) { - args := redis.Args{} - for _, v := range vals { - args = args.Add(v) - } - ids, err := redis.Values(conn.Do("SUNION", args...)) - if err != nil { - return nil, err - } - - if len(ids) == 0 { - return nil, nil - } - - objects, err := redis.ByteSlices(conn.Do("MGET", ids...)) - if err != nil { - return nil, err - } - - return objects, nil -} - -func getObjectsByValuesSorted(conn redis.Conn, limit int, vals ...string) ([][]byte, error) { - args := redis.Args{} - - cacheSet := uuid.New().String() - - args = append(args, cacheSet) - args = append(args, strconv.Itoa(len(vals))) - for _, val := range vals { - args = append(args, val) - } - - _, err := conn.Do("ZINTERSTORE", args...) - if err != nil { - return nil, err - } - - ids, err := redis.Values(conn.Do("ZREVRANGE", cacheSet, 0, -1)) - if err != nil { - return nil, err - } - - if limit < 0 || limit > len(ids) { - limit = len(ids) - } - objects, err := redis.ByteSlices(conn.Do("MGET", ids[0:limit]...)) - if err != nil { - return nil, err - } - - // clean up now unused ZINTERSTORE cache - _, err = redis.Int(conn.Do("DEL", cacheSet)) - if err != nil { - return nil, err - } - - return objects, nil -} - -func validateKeyExists(conn redis.Conn, key string) error { - count, err := redis.Int(conn.Do("EXISTS", key)) - if err != nil { - return err - } - - if count == 1 { - return nil - } - return db.ErrNotFound -} diff --git a/internal/pkg/db/redis/scheduler.go b/internal/pkg/db/redis/scheduler.go deleted file mode 100644 index 27b700ca82..0000000000 --- a/internal/pkg/db/redis/scheduler.go +++ /dev/null @@ -1,428 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 redis - -import ( - "encoding/json" - - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/pkg/db/redis/models" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/gomodule/redigo/redis" - "github.com/google/uuid" - "github.com/imdario/mergo" -) - -// Return all the schedule interval(s) -func (c *Client) Intervals() (intervals []contract.Interval, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, models.IntervalKey, 0, -1) - if err != nil { - return []contract.Interval{}, err - } - - intervals = make([]contract.Interval, len(objects)) - for i, object := range objects { - err = json.Unmarshal(object, &intervals[i]) - if err != nil { - return []contract.Interval{}, err - } - } - - return intervals, nil -} - -// Return schedule interval(s) up to the number specified -func (c *Client) IntervalsWithLimit(limit int) (intervals []contract.Interval, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, models.IntervalKey, 0, limit-1) - if err != nil { - if err != redis.ErrNil { - return intervals, err - } - } - - intervals = make([]contract.Interval, len(objects)) - for i, object := range objects { - err = json.Unmarshal(object, &intervals[i]) - if err != nil { - return []contract.Interval{}, err - } - } - - return intervals, nil -} - -// Return schedule interval by name -func (c *Client) IntervalByName(name string) (interval contract.Interval, err error) { - conn := c.Pool.Get() - defer conn.Close() - - err = getObjectByKey(conn, models.IntervalNameKey, name, &interval) - return interval, err -} - -// Return schedule interval by ID -func (c *Client) IntervalById(id string) (interval contract.Interval, err error) { - conn := c.Pool.Get() - defer conn.Close() - - object, err := redis.Bytes(conn.Do("GET", id)) - if err == redis.ErrNil { - return contract.Interval{}, db.ErrNotFound - } else if err != nil { - return contract.Interval{}, err - } - - err = json.Unmarshal(object, &interval) - if err != nil { - return contract.Interval{}, err - } - - return interval, err -} - -// Add a new schedule interval -func (c *Client) AddInterval(from contract.Interval) (id string, err error) { - interval := models.NewInterval(from) - if interval.ID != "" { - _, err = uuid.Parse(interval.ID) - if err != nil { - return "", db.ErrInvalidObjectId - } - } else { - interval.ID = uuid.New().String() - } - - if interval.Timestamps.Created == 0 { - ts := db.MakeTimestamp() - interval.Timestamps.Created = ts - interval.Timestamps.Modified = ts - } - - data, err := json.Marshal(interval) - if err != nil { - return "", err - } - - conn := c.Pool.Get() - defer conn.Close() - - _ = conn.Send("MULTI") - addObject(data, interval, interval.ID, conn) - _, err = conn.Do("EXEC") - - return interval.ID, err -} - -// Update a schedule interval -func (c *Client) UpdateInterval(from contract.Interval) (err error) { - check, err := c.IntervalByName(from.Name) - if err != nil && err != redis.ErrNil { - return err - } - if err == nil && from.ID != check.ID { - // IDs are different -> name not unique - return db.ErrNotUnique - } - - from.Timestamps.Modified = db.MakeTimestamp() - err = mergo.Merge(&from, check) - if err != nil { - return err - } - - interval := models.NewInterval(from) - data, err := json.Marshal(interval) - if err != nil { - return err - } - - conn := c.Pool.Get() - defer conn.Close() - - _ = conn.Send("MULTI") - deleteObject(interval, interval.ID, conn) - addObject(data, interval, interval.ID, conn) - _, err = conn.Do("EXEC") - - return err -} - -// Remove schedule interval by ID -func (c *Client) DeleteIntervalById(id string) (err error) { - check, err := c.IntervalById(id) - if err != nil { - if err == db.ErrNotFound { - return nil - } - return - } - - interval := models.NewInterval(check) - conn := c.Pool.Get() - defer conn.Close() - - _ = conn.Send("MULTI") - deleteObject(interval, id, conn) - - _, err = conn.Do("EXEC") - - return err -} - -// Scrub all scheduler intervals from the database (only used in test) -func (c *Client) ScrubAllIntervals() (count int, err error) { - conn := c.Pool.Get() - defer conn.Close() - - cols := []string{models.IntervalKey} - - for _, col := range cols { - err = unlinkCollection(conn, col) - if err != nil { - return -1, err - } - } - - return 0, nil -} - -// Get all schedule interval action(s) -func (c *Client) IntervalActions() (actions []contract.IntervalAction, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, models.IntervalActionKey, 0, -1) - if err != nil { - return []contract.IntervalAction{}, err - } - - actions = make([]contract.IntervalAction, len(objects)) - for i, object := range objects { - err = json.Unmarshal(object, &actions[i]) - if err != nil { - return []contract.IntervalAction{}, err - } - } - - return actions, nil -} - -// Return schedule interval action(s) up to the number specified -func (c *Client) IntervalActionsWithLimit(limit int) (actions []contract.IntervalAction, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByRange(conn, models.IntervalActionKey, 0, limit-1) - if err != nil { - if err != redis.ErrNil { - return actions, err - } - } - - actions = make([]contract.IntervalAction, len(objects)) - for i, object := range objects { - err = json.Unmarshal(object, &actions[i]) - if err != nil { - return []contract.IntervalAction{}, err - } - } - - return actions, nil -} - -// Get all schedule interval action(s) by interval name -func (c *Client) IntervalActionsByIntervalName(name string) (actions []contract.IntervalAction, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByValue(conn, models.IntervalActionParentKey+":"+name) - if err != nil { - if err != redis.ErrNil { - return actions, err - } - } - - actions = make([]contract.IntervalAction, len(objects)) - for i, action := range objects { - err = unmarshalObject(action, &actions[i]) - if err != nil { - return actions, err - } - } - return actions, err -} - -// Get all schedule interval action(s) by target name -func (c *Client) IntervalActionsByTarget(name string) (actions []contract.IntervalAction, err error) { - conn := c.Pool.Get() - defer conn.Close() - - objects, err := getObjectsByValue(conn, models.IntervalActionTargetKey+":"+name) - if err != nil { - if err != redis.ErrNil { - return actions, err - } - } - - actions = make([]contract.IntervalAction, len(objects)) - for i, action := range objects { - err = unmarshalObject(action, &actions[i]) - if err != nil { - return actions, err - } - } - return actions, err -} - -// Get schedule interval action by id -func (c *Client) IntervalActionById(id string) (action contract.IntervalAction, err error) { - conn := c.Pool.Get() - defer conn.Close() - - object, err := redis.Bytes(conn.Do("GET", id)) - if err == redis.ErrNil { - return contract.IntervalAction{}, db.ErrNotFound - } else if err != nil { - return contract.IntervalAction{}, err - } - - err = json.Unmarshal(object, &action) - if err != nil { - return contract.IntervalAction{}, err - } - - return action, err -} - -// Get schedule interval action by name -func (c *Client) IntervalActionByName(name string) (action contract.IntervalAction, err error) { - conn := c.Pool.Get() - defer conn.Close() - - err = getObjectByKey(conn, models.IntervalActionNameKey, name, &action) - return action, err -} - -// Add schedule interval action -func (c *Client) AddIntervalAction(from contract.IntervalAction) (id string, err error) { - action := models.NewIntervalAction(from) - if action.ID != "" { - _, err = uuid.Parse(action.ID) - if err != nil { - return "", db.ErrInvalidObjectId - } - } else { - action.ID = uuid.New().String() - } - - if action.Created == 0 { - ts := db.MakeTimestamp() - action.Created = ts - action.Modified = ts - } - - data, err := json.Marshal(action) - if err != nil { - return "", err - } - - conn := c.Pool.Get() - defer conn.Close() - - _ = conn.Send("MULTI") - addObject(data, action, action.ID, conn) - _, err = conn.Do("EXEC") - - return action.ID, err -} - -// Update schedule interval action -func (c *Client) UpdateIntervalAction(from contract.IntervalAction) (err error) { - check, err := c.IntervalActionByName(from.Name) - if err != nil && err != redis.ErrNil { - return err - } - if err == nil && from.ID != check.ID { - // IDs are different -> name not unique - return db.ErrNotUnique - } - - from.Modified = db.MakeTimestamp() - err = mergo.Merge(&from, check) - if err != nil { - return err - } - - action := models.NewIntervalAction(from) - data, err := json.Marshal(action) - if err != nil { - return err - } - - conn := c.Pool.Get() - defer conn.Close() - - _ = conn.Send("MULTI") - deleteObject(action, action.ID, conn) - addObject(data, action, action.ID, conn) - _, err = conn.Do("EXEC") - - return err -} - -// Remove schedule interval action by id -func (c *Client) DeleteIntervalActionById(id string) (err error) { - check, err := c.IntervalActionById(id) - if err != nil { - if err == db.ErrNotFound { - return nil - } - return - } - - action := models.NewIntervalAction(check) - conn := c.Pool.Get() - defer conn.Close() - - _ = conn.Send("MULTI") - deleteObject(action, id, conn) - - _, err = conn.Do("EXEC") - - return err -} - -// Scrub all scheduler interval actions from the database data (only used in test) -func (c *Client) ScrubAllIntervalActions() (count int, err error) { - conn := c.Pool.Get() - defer conn.Close() - - cols := []string{models.IntervalActionKey} - - for _, col := range cols { - err = unlinkCollection(conn, col) - if err != nil { - return -1, err - } - } - - return 0, nil -} diff --git a/internal/pkg/db/redis/scripts.go b/internal/pkg/db/redis/scripts.go deleted file mode 100644 index b4b8a654aa..0000000000 --- a/internal/pkg/db/redis/scripts.go +++ /dev/null @@ -1,152 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Redis Labs Inc. - * - * 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 redis - -import "github.com/gomodule/redigo/redis" - -/*********************** - * Lua scripts notes: - * magic number 4096 is less < 8000 (redis:/deps/lua/lapi.c:LUAI_MAXCSTACK -> unpack error) - * assumes a single instance - * `get*` scripts are implementations for range operations. Can be used when the server is - * remote in order to reduce latency. - */ - -const ( - scriptGetObjectsByRange = ` - local magic = 4096 - local ids = redis.call('ZRANGE', KEYS[1], ARGV[1], ARGV[2]) - local rep = {} - if #ids > 0 then - for i = 1, #ids, magic do - local temp = redis.call('MGET', unpack(ids, i, i+magic < #ids and i+magic or #ids)) - for _, o in ipairs(temp) do - table.insert(rep, o) - end - end - return rep - else - return nil - end - ` - scriptGetObjectsByRangeFilter = ` - local magic = 4096 - local ids = redis.call('ZRANGE', KEYS[1], ARGV[1], ARGV[2]) - local rep = {} - if #ids > 0 then - for i, id in ipairs(ids) do - local v = redis.call('ZSCORE', KEYS[2], id) - if v == nil then - ids[i] = nil - end - end - for i = 1, #ids, magic do - local temp = redis.call('MGET', unpack(ids, i, i+magic < #ids and i+magic or #ids)) - for _, o in ipairs(temp) do - table.insert(rep, o) - end - end - else - return nil - end - return rep - ` - scriptGetObjectsByScore = ` - local magic = 4096 - local cmd = { - 'ZRANGEBYSCORE', KEYS[1], ARGV[1], - tonumber(ARGV[2]) < 0 and '+inf' or ARGV[2], - } - if tonumber(ARGV[3]) ~= 0 then - table.insert(cmd, 'LIMIT') - table.insert(cmd, 0) - table.insert(cmd, ARGV[3]) - end - local ids = redis.call(unpack(cmd)) - local rep = {} - if #ids > 0 then - for i = 1, #ids, magic do - local temp = redis.call('MGET', unpack(ids, i, i+magic < #ids and i+magic or #ids)) - for _, o in ipairs(temp) do - table.insert(rep, o) - end - end - else - return nil - end - return rep - ` - scriptUnlinkZsetMembers = ` - local magic = 4096 - local ids = redis.call('ZRANGE', KEYS[1], 0, -1) - if #ids > 0 then - for i = 1, #ids, magic do - redis.call('UNLINK', unpack(ids, i, i+magic < #ids and i+magic or #ids)) - end - end - ` - scriptUnlinkCollection = ` - local magic = 4096 - redis.replicate_commands() - local c = 0 - repeat - local s = redis.call('SCAN', c, 'MATCH', ARGV[1] .. '*') - c = tonumber(s[1]) - if #s[2] > 0 then - redis.call('UNLINK', unpack(s[2])) - end - until c == 0 - ` -) - -var scripts = map[string]redis.Script{ - "getObjectsByRange": *redis.NewScript(1, scriptGetObjectsByRange), - "getObjectsByRangeFilter": *redis.NewScript(2, scriptGetObjectsByRangeFilter), - "getObjectsByScore": *redis.NewScript(1, scriptGetObjectsByScore), - "unlinkZsetMembers": *redis.NewScript(1, scriptUnlinkZsetMembers), - "unlinkCollection": *redis.NewScript(0, scriptUnlinkCollection), -} - -func getObjectsByRangeLua(conn redis.Conn, key string, start, end int) (objects [][]byte, err error) { - s := scripts["getObjectsByRange"] - objects, err = redis.ByteSlices(s.Do(conn, key, start, end)) - if err != nil { - return nil, err - } - - return objects, nil -} - -func getObjectsByRangeFilterLua(conn redis.Conn, key string, filter string, start, end int) (objects [][]byte, err error) { - s := scripts["getObjectsByRangeFilter"] - objects, err = redis.ByteSlices(s.Do(conn, key, filter, start, end)) - if err != nil { - return nil, err - } - - return objects, nil -} - -// Return objects by a score from a zset -// if limit is 0, all are returned -// if end is negative, it is considered as positive infinity -func getObjectsByScoreLua(conn redis.Conn, key string, start, end int64, limit int) (objects [][]byte, err error) { - s := scripts["getObjectsByScore"] - objects, err = redis.ByteSlices(s.Do(conn, key, start, end, limit)) - if err != nil { - return nil, err - } - - return objects, nil -} diff --git a/internal/pkg/db/redis/util.go b/internal/pkg/db/redis/util.go deleted file mode 100644 index 88826351b4..0000000000 --- a/internal/pkg/db/redis/util.go +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Redis Labs Inc. - * - * 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 redis - -import "github.com/gomodule/redigo/redis" - -func unlinkCollection(conn redis.Conn, col string) error { - _ = conn.Send("MULTI") - s := scripts["unlinkZsetMembers"] - _ = s.Send(conn, col) - s = scripts["unlinkCollection"] - _ = s.Send(conn, col) - _, err := conn.Do("EXEC") - return err -} diff --git a/internal/pkg/db/test/db_data.go b/internal/pkg/db/test/db_data.go deleted file mode 100644 index 87a90d9488..0000000000 --- a/internal/pkg/db/test/db_data.go +++ /dev/null @@ -1,900 +0,0 @@ -// -// Copyright (c) 2018 Cavium -// -// SPDX-License-Identifier: Apache-2.0 -// - -package test - -import ( - "fmt" - "math" - "strconv" - "testing" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/stretchr/testify/require" - - "github.com/edgexfoundry/edgex-go/internal/core/data/interfaces" - correlation "github.com/edgexfoundry/edgex-go/internal/pkg/correlation/models" - dbp "github.com/edgexfoundry/edgex-go/internal/pkg/db" -) - -func populateDbReadings(db interfaces.DBClient, count int) (string, error) { - var id string - for i := 0; i < count; i++ { - name := fmt.Sprintf("name%d", i) - r := contract.Reading{} - r.Name = name - r.Device = name - r.Value = name - var err error - id, err = db.AddReading(r) - if err != nil { - return id, err - } - } - return id, nil -} - -func populateDbValues(db interfaces.DBClient, count int) (string, error) { - var id string - for i := 0; i < count; i++ { - name := fmt.Sprintf("name%d", i) - v := contract.ValueDescriptor{} - v.Name = name - v.Description = name - v.Type = name - v.UomLabel = name - v.Labels = []string{name, "LABEL"} - var err error - id, err = db.AddValueDescriptor(v) - if err != nil { - return id, err - } - } - return id, nil -} - -func populateDbEvents(db interfaces.DBClient, count int, pushed int64) (string, error) { - var id string - for i := 0; i < count; i++ { - name := fmt.Sprintf("name%d", i) - e := correlation.Event{} - e.Device = name - e.Pushed = pushed - var err error - id, err = db.AddEvent(e) - if err != nil { - return id, err - } - } - return id, nil -} - -func testDBReadings(t *testing.T, db interfaces.DBClient) { - err := db.ScrubAllEvents() - if err != nil { - t.Fatalf("Error removing all readings: %v\n", err) - } - - readings, err := db.Readings() - if err != nil { - t.Fatalf("Error getting readings %v", err) - } - if readings == nil { - t.Fatalf("Should return an empty array") - } - if len(readings) != 0 { - t.Fatalf("There should be 0 readings instead of %d", len(readings)) - } - - beforeTime := dbp.MakeTimestamp() - id, err := populateDbReadings(db, 100) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - - // To have two readings with the same name - id, err = populateDbReadings(db, 10) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - afterTime := dbp.MakeTimestamp() - - count, err := db.ReadingCount() - if err != nil { - t.Fatalf("Error getting readings count: %v", err) - } - if count != 110 { - t.Fatalf("There should be 110 readings instead of %d", count) - } - - readings, err = db.Readings() - if err != nil { - t.Fatalf("Error getting readings %v", err) - } - if len(readings) != 110 { - t.Fatalf("There should be 110 readings instead of %d", len(readings)) - } - - var lastModified int64 = math.MaxInt64 - - for _, r := range readings { - if r.Modified > lastModified { - t.Fatal("Readings should be sorted in descending order by timestamp") - } - - lastModified = r.Modified - } - - r3, err := db.ReadingById(id) - if err != nil { - t.Fatalf("Error getting reading by id %v", err) - } - if r3.Id != id { - t.Fatalf("Id does not match %s - %s", r3.Id, id) - } - _, err = db.ReadingById("INVALID") - if err == nil { - t.Fatalf("Reading should not be found") - } - - readings, err = db.ReadingsByDeviceAndValueDescriptor("name1", "name1", 10) - if err != nil { - t.Fatalf("Error getting ReadingsByDeviceAndValueDescriptor: %v", err) - } - if len(readings) != 2 { - t.Fatalf("There should be 2 readings, not %d", len(readings)) - } - readings, err = db.ReadingsByDeviceAndValueDescriptor("name1", "name1", 1) - if err != nil { - t.Fatalf("Error getting ReadingsByDeviceAndValueDescriptor: %v", err) - } - if len(readings) != 1 { - t.Fatalf("There should be 1 readings, not %d", len(readings)) - } - readings, err = db.ReadingsByDeviceAndValueDescriptor("name20", "name20", 10) - if err != nil { - t.Fatalf("Error getting ReadingsByDeviceAndValueDescriptor: %v", err) - } - if len(readings) != 1 { - t.Fatalf("There should be 1 readings, not %d", len(readings)) - } - - readings, err = db.ReadingsByDevice("name1", 10) - if err != nil { - t.Fatalf("Error getting ReadingsByDevice: %v", err) - } - if len(readings) != 2 { - t.Fatalf("There should be 2 readings, not %d", len(readings)) - } - readings, err = db.ReadingsByDevice("name1", 1) - if err != nil { - t.Fatalf("Error getting ReadingsByDevice: %v", err) - } - if len(readings) != 1 { - t.Fatalf("There should be 1 readings, not %d", len(readings)) - } - readings, err = db.ReadingsByDevice("name20", 10) - if err != nil { - t.Fatalf("Error getting ReadingsByDevice: %v", err) - } - if len(readings) != 1 { - t.Fatalf("There should be 1 readings, not %d", len(readings)) - } - - readings, err = db.ReadingsByValueDescriptor("name1", 10) - if err != nil { - t.Fatalf("Error getting ReadingsByValueDescriptor: %v", err) - } - if len(readings) != 2 { - t.Fatalf("There should be 2 readings, not %d", len(readings)) - } - readings, err = db.ReadingsByValueDescriptor("name1", 1) - if err != nil { - t.Fatalf("Error getting ReadingsByValueDescriptor: %v", err) - } - if len(readings) != 1 { - t.Fatalf("There should be 1 readings, not %d", len(readings)) - } - readings, err = db.ReadingsByValueDescriptor("name20", 10) - if err != nil { - t.Fatalf("Error getting ReadingsByValueDescriptor: %v", err) - } - if len(readings) != 1 { - t.Fatalf("There should be 1 readings, not %d", len(readings)) - } - readings, err = db.ReadingsByValueDescriptor("name", 10) - if err != nil { - t.Fatalf("Error getting ReadingsByValueDescriptor: %v", err) - } - if len(readings) != 0 { - t.Fatalf("There should be 0 readings, not %d", len(readings)) - } - - readings, err = db.ReadingsByValueDescriptorNames([]string{"name1", "name2"}, 10) - if err != nil { - t.Fatalf("Error getting ReadingsByValueDescriptorNames: %v", err) - } - if len(readings) != 4 { - t.Fatalf("There should be 4 readings, not %d", len(readings)) - } - readings, err = db.ReadingsByValueDescriptorNames([]string{"name1", "name2"}, 1) - if err != nil { - t.Fatalf("Error getting ReadingsByValueDescriptorNames: %v", err) - } - if len(readings) != 1 { - t.Fatalf("There should be 1 readings, not %d", len(readings)) - } - readings, err = db.ReadingsByValueDescriptorNames([]string{"name", "noname"}, 10) - if err != nil { - t.Fatalf("Error getting ReadingsByValueDescriptorNames: %v", err) - } - if len(readings) != 0 { - t.Fatalf("There should be 0 readings, not %d", len(readings)) - } - readings, err = db.ReadingsByValueDescriptorNames([]string{"name20"}, 10) - if err != nil { - t.Fatalf("Error getting ReadingsByValueDescriptorNames: %v", err) - } - if len(readings) != 1 { - t.Fatalf("There should be 1 readings, not %d", len(readings)) - } - - readings, err = db.ReadingsByCreationTime(beforeTime, afterTime, 200) - if err != nil { - t.Fatalf("Error getting ReadingsByCreationTime: %v", err) - } - if len(readings) != 110 { - t.Fatalf("There should be 110 readings, not %d", len(readings)) - } - readings, err = db.ReadingsByCreationTime(beforeTime, afterTime, 100) - if err != nil { - t.Fatalf("Error getting ReadingsByCreationTime: %v", err) - } - if len(readings) != 100 { - t.Fatalf("There should be 100 readings, not %d", len(readings)) - } - - r := contract.Reading{} - r.Id = id - r.Name = "name" - err = db.UpdateReading(r) - if err != nil { - t.Fatalf("Error updating reading %v", err) - } - r2, err := db.ReadingById(r.Id) - if err != nil { - t.Fatalf("Error getting reading by id %v", err) - } - if r2.Name != r.Name { - t.Fatalf("Did not update reading correctly: %s %s", r.Name, r2.Name) - } - - err = db.DeleteReadingById("INVALID") - if err == nil { - t.Fatalf("Reading should not be deleted") - } - - err = db.DeleteReadingById(id) - if err != nil { - t.Fatalf("Reading should be deleted: %v", err) - } - - err = db.UpdateReading(r) - if err == nil { - t.Fatalf("Update should return error") - } -} - -func testDBEvents(t *testing.T, db interfaces.DBClient) { - err := db.ScrubAllEvents() - if err != nil { - t.Fatalf("Error removing all events") - } - - events, err := db.Events() - if err != nil { - t.Fatalf("Error getting events %v", err) - } - - if len(events) != 0 { - t.Fatalf("There should be 0 events instead of %d", len(events)) - } - - beforeTime := dbp.MakeTimestamp() - id, err := populateDbEvents(db, 100, 0) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - - // To have two events with the same name - id, err = populateDbEvents(db, 10, 1) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - afterTime := dbp.MakeTimestamp() - - count, err := db.EventCount() - if err != nil { - t.Fatalf("Error getting events count: %v", err) - } - if count != 110 { - t.Fatalf("There should be 110 events instead of %d", count) - } - - count, err = db.EventCountByDeviceId("name1") - if err != nil { - t.Fatalf("Error getting events count: %v", err) - } - if count != 2 { - t.Fatalf("There should be 2 events instead of %d", count) - } - - count, err = db.EventCountByDeviceId("name20") - if err != nil { - t.Fatalf("Error getting events count: %v", err) - } - if count != 1 { - t.Fatalf("There should be 1 events instead of %d", count) - } - - count, err = db.EventCountByDeviceId("name") - if err != nil { - t.Fatalf("Error getting events count: %v", err) - } - if count != 0 { - t.Fatalf("There should be 0 events instead of %d", count) - } - - events, err = db.Events() - if err != nil { - t.Fatalf("Error getting events %v", err) - } - if len(events) != 110 { - t.Fatalf("There should be 110 events instead of %d", len(events)) - } - e3, err := db.EventById(id) - if err != nil { - t.Fatalf("Error getting event by id %v", err) - } - if e3.ID != id { - t.Fatalf("Id does not match %s - %s", e3.ID, id) - } - _, err = db.EventById("INVALID") - if err == nil { - t.Fatalf("Event should not be found") - } - - events, err = db.EventsForDeviceLimit("name1", 10) - if err != nil { - t.Fatalf("Error getting EventsForDeviceLimit: %v", err) - } - if len(events) != 2 { - t.Fatalf("There should be 2 events, not %d", len(events)) - } - events, err = db.EventsForDeviceLimit("name1", 1) - if err != nil { - t.Fatalf("Error getting EventsForDeviceLimit: %v", err) - } - if len(events) != 1 { - t.Fatalf("There should be 1 events, not %d", len(events)) - } - events, err = db.EventsForDeviceLimit("name20", 10) - if err != nil { - t.Fatalf("Error getting EventsForDeviceLimit: %v", err) - } - if len(events) != 1 { - t.Fatalf("There should be 1 events, not %d", len(events)) - } - events, err = db.EventsForDeviceLimit("name", 10) - if err != nil { - t.Fatalf("Error getting EventsForDeviceLimit: %v", err) - } - if len(events) != 0 { - t.Fatalf("There should be 0 events, not %d", len(events)) - } - - events, err = db.EventsForDevice("name1") - if err != nil { - t.Fatalf("Error getting EventsForDevice: %v", err) - } - if len(events) != 2 { - t.Fatalf("There should be 2 events, not %d", len(events)) - } - events, err = db.EventsForDevice("name20") - if err != nil { - t.Fatalf("Error getting EventsForDevice: %v", err) - } - if len(events) != 1 { - t.Fatalf("There should be 1 events, not %d", len(events)) - } - events, err = db.EventsForDevice("name") - if err != nil { - t.Fatalf("Error getting EventsForDevice: %v", err) - } - if len(events) != 0 { - t.Fatalf("There should be 0 events, not %d", len(events)) - } - - events, err = db.EventsByCreationTime(beforeTime, afterTime, 200) - if err != nil { - t.Fatalf("Error getting EventsByCreationTime: %v", err) - } - if len(events) != 110 { - t.Fatalf("There should be 110 events, not %d", len(events)) - } - events, err = db.EventsByCreationTime(beforeTime, afterTime, 100) - if err != nil { - t.Fatalf("Error getting EventsByCreationTime: %v", err) - } - if len(events) != 100 { - t.Fatalf("There should be 100 events, not %d", len(events)) - } - - events, err = db.EventsOlderThanAge(0) - if err != nil { - t.Fatalf("Error getting EventsOlderThanAge: %v", err) - } - if len(events) != 110 { - t.Fatalf("There should be 110 events, not %d", len(events)) - } - events, err = db.EventsOlderThanAge(1000000) - if err != nil { - t.Fatalf("Error getting EventsOlderThanAge: %v", err) - } - if len(events) != 0 { - t.Fatalf("There should be 0 events, not %d", len(events)) - } - - events, err = db.EventsPushed() - if err != nil { - t.Fatalf("Error getting EventsOlderThanAge: %v", err) - } - if len(events) != 10 { - t.Fatalf("There should be 10 events, not %d", len(events)) - } - - e := correlation.Event{} - e.ID = id - e.Device = "name" - err = db.UpdateEvent(e) - if err != nil { - t.Fatalf("Error updating event %v", err) - } - e2, err := db.EventById(e.ID) - if err != nil { - t.Fatalf("Error getting event by id %v", err) - } - if e2.Device != e.Device { - t.Fatalf("Did not update event correctly: %s %s", e.Device, e2.Device) - } - - err = db.DeleteEventById("INVALID") - if err == nil { - t.Fatalf("Event should not be deleted") - } - - err = db.DeleteEventById(id) - if err != nil { - t.Fatalf("Event should be deleted: %v", err) - } - - err = db.UpdateEvent(e) - if err == nil { - t.Fatalf("Update should return error") - } - - err = db.ScrubAllEvents() - if err != nil { - t.Fatalf("Error removing all events") - } - - events, err = db.Events() - if err != nil { - t.Fatalf("Error getting events %v", err) - } - if len(events) != 0 { - t.Fatalf("There should be 0 events instead of %d", len(events)) - } -} - -func testDBValueDescriptors(t *testing.T, db interfaces.DBClient) { - err := db.ScrubAllValueDescriptors() - if err != nil { - t.Fatalf("Error removing all value descriptors") - } - - values, err := db.ValueDescriptors() - if err != nil { - t.Fatalf("Error getting events %v", err) - } - if len(values) != 0 { - t.Fatalf("There should be 0 values instead of %d", len(values)) - } - - id, err := populateDbValues(db, 110) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - - _, err = populateDbValues(db, 110) - if err == nil { - t.Fatalf("Should be an error adding a new ValueDescriptor with the same name\n") - } - - values, err = db.ValueDescriptors() - if err != nil { - t.Fatalf("Error getting Values %v", err) - } - if len(values) != 110 { - t.Fatalf("There should be 110 Values instead of %d", len(values)) - } - - v3, err := db.ValueDescriptorById(id) - if err != nil { - t.Fatalf("Error getting Value by id %v", err) - } - if v3.Id != id { - t.Fatalf("Id does not match %s - %s", v3.Id, id) - } - _, err = db.ValueDescriptorById("INVALID") - if err == nil { - t.Fatalf("Value should not be found") - } - - v3, err = db.ValueDescriptorByName("name1") - if err != nil { - t.Fatalf("Error getting Value by id %v", err) - } - if v3.Name != "name1" { - t.Fatalf("Name does not match %s - name1", v3.Name) - } - _, err = db.ValueDescriptorByName("INVALID") - if err == nil { - t.Fatalf("Value should not be found") - } - - values, err = db.ValueDescriptorsByName([]string{"name1", "name2"}) - if err != nil { - t.Fatalf("Error getting ValueDescriptorsByName: %v", err) - } - if len(values) != 2 { - t.Fatalf("There should be 2 Values, not %d", len(values)) - } - values, err = db.ValueDescriptorsByName([]string{"name1", "name"}) - if err != nil { - t.Fatalf("Error getting ValueDescriptorsByName: %v", err) - } - if len(values) != 1 { - t.Fatalf("There should be 1 Values, not %d", len(values)) - } - values, err = db.ValueDescriptorsByName([]string{"name", "INVALID"}) - if err != nil { - t.Fatalf("Error getting ValueDescriptorsByName: %v", err) - } - if len(values) != 0 { - t.Fatalf("There should be 0 Values, not %d", len(values)) - } - - values, err = db.ValueDescriptorsByUomLabel("name1") - if err != nil { - t.Fatalf("Error getting ValueDescriptorsByUomLabel: %v", err) - } - if len(values) != 1 { - t.Fatalf("There should be 1 Values, not %d", len(values)) - } - values, err = db.ValueDescriptorsByUomLabel("INVALID") - if err != nil { - t.Fatalf("Error getting ValueDescriptorsByLabel: %v", err) - } - if len(values) != 0 { - t.Fatalf("There should be 0 Values, not %d", len(values)) - } - - values, err = db.ValueDescriptorsByLabel("name1") - if err != nil { - t.Fatalf("Error getting ValueDescriptorsByLabel: %v", err) - } - if len(values) != 1 { - t.Fatalf("There should be 1 Values, not %d", len(values)) - } - values, err = db.ValueDescriptorsByLabel("INVALID") - if err != nil { - t.Fatalf("Error getting ValueDescriptorsByLabel: %v", err) - } - if len(values) != 0 { - t.Fatalf("There should be 0 Values, not %d", len(values)) - } - - values, err = db.ValueDescriptorsByType("name1") - if err != nil { - t.Fatalf("Error getting ValueDescriptorsByType: %v", err) - } - if len(values) != 1 { - t.Fatalf("There should be 1 Values, not %d", len(values)) - } - values, err = db.ValueDescriptorsByType("INVALID") - if err != nil { - t.Fatalf("Error getting ValueDescriptorsByType: %v", err) - } - if len(values) != 0 { - t.Fatalf("There should be 0 Values, not %d", len(values)) - } - - v := contract.ValueDescriptor{} - v.Id = id - v.Name = "name" - err = db.UpdateValueDescriptor(v) - if err != nil { - t.Fatalf("Error updating Value %v", err) - } - v2, err := db.ValueDescriptorById(v.Id) - if err != nil { - t.Fatalf("Error getting Value by id %v", err) - } - if v2.Name != v.Name { - t.Fatalf("Did not update Value correctly: %s %s", v.Name, v2.Name) - } - - err = db.DeleteValueDescriptorById("INVALID") - if err == nil { - t.Fatalf("Value should not be deleted") - } - - err = db.DeleteValueDescriptorById(id) - if err != nil { - t.Fatalf("Value should be deleted: %v", err) - } - - err = db.UpdateValueDescriptor(v) - if err == nil { - t.Fatalf("Update should return error") - } - - err = db.ScrubAllValueDescriptors() - if err != nil { - t.Fatalf("Error removing all value descriptors") - } -} - -func testBinaryEvent(t *testing.T, db interfaces.DBClient) { - eventWithBinaryReading := correlation.Event{ - CorrelationId: "", - Event: contract.Event{ - Device: "BinaryDevice", - Readings: []contract.Reading{ - { - Name: "BinaryReading", - ValueType: contract.ValueTypeBinary, - BinaryValue: []byte{1}, - MediaType: "cbor", - }, - }, - }, - } - - // Ensure an Add does not persist Binary data - id, err := db.AddEvent(eventWithBinaryReading) - require.NoError(t, err) - persistedEventFromDB, err := db.EventById(id) - require.NoError(t, err) - for _, reading := range persistedEventFromDB.Readings { - require.Empty(t, reading.BinaryValue) - } - - // Ensure an Update does not persist Binary data - eventWithBinaryReading.ID = persistedEventFromDB.ID - eventWithBinaryReading.Device = "AnotherBinaryDevice" - err = db.UpdateEvent(eventWithBinaryReading) - require.NoError(t, err) - persistedEventFromDB, err = db.EventById(id) - require.NoError(t, err) - for _, reading := range persistedEventFromDB.Readings { - require.Empty(t, reading.BinaryValue) - } -} - -func testBinaryReading(t *testing.T, db interfaces.DBClient) { - readingWithBinaryReading := contract.Reading{ - Name: "BinaryReading", - ValueType: contract.ValueTypeBinary, - BinaryValue: []byte{1}, - MediaType: "cbor", - } - - // Ensure an Add does not persist Binary data - id, err := db.AddReading(readingWithBinaryReading) - require.NoError(t, err) - persistedReadingFromDB, err := db.ReadingById(id) - require.NoError(t, err) - require.Empty(t, persistedReadingFromDB.BinaryValue) - - // Ensure an Update does not persist Binary data - readingWithBinaryReading.Id = persistedReadingFromDB.Id - readingWithBinaryReading.Device = "AnotherBinaryDevice" - err = db.UpdateReading(readingWithBinaryReading) - require.NoError(t, err) - persistedReadingFromDB, err = db.ReadingById(id) - require.NoError(t, err) - require.Empty(t, persistedReadingFromDB.BinaryValue) -} - -func TestDataDB(t *testing.T, db interfaces.DBClient) { - testDBReadings(t, db) - testDBEvents(t, db) - testDBValueDescriptors(t, db) - testBinaryEvent(t, db) - testBinaryReading(t, db) - - db.CloseSession() - // Calling CloseSession twice to test that there is no panic when closing an - // already closed db - db.CloseSession() -} - -func BenchmarkDB(b *testing.B, db interfaces.DBClient) { - - benchmarkReadings(b, db) - benchmarkEvents(b, db) - db.CloseSession() -} - -func benchmarkReadings(b *testing.B, db interfaces.DBClient) { - - // Remove previous events and readings - db.ScrubAllEvents() - - b.Run("AddReading", func(b *testing.B) { - reading := contract.Reading{} - for i := 0; i < b.N; i++ { - reading.Name = "test" + strconv.Itoa(i) - reading.Device = "device" + strconv.Itoa(i/100) - _, err := db.AddReading(reading) - if err != nil { - b.Fatalf("Error add reading: %v", err) - } - } - }) - - // Remove previous events and readings - db.ScrubAllEvents() - // prepare to benchmark n readings - n := 1000 - readings := make([]string, n) - reading := contract.Reading{} - for i := 0; i < n; i++ { - reading.Name = "test" + strconv.Itoa(i) - reading.Device = "device" + strconv.Itoa(i/100) - id, err := db.AddReading(reading) - if err != nil { - b.Fatalf("Error add reading: %v", err) - } - readings[i] = id - } - - b.Run("Readings", func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, err := db.Readings() - if err != nil { - b.Fatalf("Error readings: %v", err) - } - } - }) - - b.Run("ReadingCount", func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, err := db.ReadingCount() - if err != nil { - b.Fatalf("Error reading count: %v", err) - } - } - }) - - b.Run("ReadingById", func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, err := db.ReadingById(readings[i%len(readings)]) - if err != nil { - b.Fatalf("Error reading by ID: %v", err) - } - } - }) - - b.Run("ReadingsByDevice", func(b *testing.B) { - for i := 0; i < b.N; i++ { - device := "device" + strconv.Itoa((i%len(readings))/100) - _, err := db.ReadingsByDevice(device, 100) - if err != nil { - b.Fatalf("Error reading by device: %v", err) - } - } - }) -} - -func benchmarkEvents(b *testing.B, db interfaces.DBClient) { - - // Remove previous events and readings - db.ScrubAllEvents() - - b.Run("AddEvent", func(b *testing.B) { - for i := 0; i < b.N; i++ { - device := fmt.Sprintf("device" + strconv.Itoa(i/100)) - e := correlation.Event{} - e.Device = device - - for j := 0; j < 5; j++ { - r := contract.Reading{ - Device: device, - Name: fmt.Sprintf("name%d", j), - } - e.Readings = append(e.Readings, r) - } - _, err := db.AddEvent(e) - if err != nil { - b.Fatalf("Error add event: %v", err) - } - } - }) - - // Remove previous events and readings - db.ScrubAllEvents() - // prepare to benchmark n events (5 readings each) - n := 1000 - events := make([]string, n) - for i := 0; i < n; i++ { - device := fmt.Sprintf("device" + strconv.Itoa(i/100)) - e := correlation.Event{} - e.Device = device - - for j := 0; j < 5; j++ { - r := contract.Reading{ - Device: device, - Name: fmt.Sprintf("name%d", j), - } - e.Readings = append(e.Readings, r) - } - id, err := db.AddEvent(e) - if err != nil { - b.Fatalf("Error add event: %v", err) - } - events[i] = id - } - - b.Run("Events", func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, err := db.Events() - if err != nil { - b.Fatalf("Error events: %v", err) - } - } - }) - - b.Run("EventCount", func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, err := db.EventCount() - if err != nil { - b.Fatalf("Error event count: %v", err) - } - } - }) - - b.Run("EventById", func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, err := db.EventById(events[i%len(events)]) - if err != nil { - b.Fatalf("Error event by ID: %v", err) - } - } - }) - - b.Run("EventsForDevice", func(b *testing.B) { - for i := 0; i < b.N; i++ { - device := "device" + strconv.Itoa(i%len(events)/100) - _, err := db.EventsForDevice(device) - if err != nil { - b.Fatalf("Error events for device: %v", err) - } - } - }) -} diff --git a/internal/pkg/db/test/db_metadata.go b/internal/pkg/db/test/db_metadata.go deleted file mode 100644 index 3e8b5cd90a..0000000000 --- a/internal/pkg/db/test/db_metadata.go +++ /dev/null @@ -1,1154 +0,0 @@ -// -// Copyright (c) 2018 Cavium -// -// SPDX-License-Identifier: Apache-2.0 -// - -package test - -import ( - "fmt" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/core/metadata/interfaces" - dataBase "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/google/uuid" - "github.com/stretchr/testify/require" -) - -func TestMetadataDB(t *testing.T, db interfaces.DBClient) { - // Remove previous metadata - db.ScrubMetadata() - - testDBAddressables(t, db) - testDBDeviceService(t, db) - testDBDeviceReport(t, db) - testDBDeviceProfile(t, db) - testDBDevice(t, db) - testDBCommand(t, db) - testDBProvisionWatcher(t, db) - - db.CloseSession() - // Calling CloseSession twice to test that there is no panic when closing an - // already closed db - db.CloseSession() -} - -func getProtocols() map[string]models.ProtocolProperties { - p1 := make(map[string]string) - p1["host"] = "localhost" - p1["port"] = "1234" - p1["unitID"] = "1" - - p2 := make(map[string]string) - p2["serialPort"] = "/dev/USB0" - p2["baudRate"] = "19200" - p2["dataBits"] = "8" - p2["stopBits"] = "1" - p2["parity"] = "0" - p2["unitID"] = "2" - - wrap := make(map[string]models.ProtocolProperties) - wrap["modbus-ip"] = p1 - wrap["modbus-rtu"] = p2 - - return wrap -} - -func getAddressable(i int, prefix string) models.Addressable { - name := fmt.Sprintf("%sname%d", prefix, i) - a := models.Addressable{} - - a.Name = name - a.Protocol = name - a.HTTPMethod = name - a.Address = name - a.Port = i - a.Path = name - a.Publisher = name - a.User = name - a.Password = name - a.Topic = name - return a -} - -func getDeviceService(db interfaces.DBClient, i int) (models.DeviceService, error) { - name := fmt.Sprintf("name%d", i) - ds := models.DeviceService{} - ds.Name = name - ds.AdminState = "UNLOCKED" - ds.Addressable = getAddressable(i, "ds_") - ds.Labels = append(ds.Labels, name) - ds.OperatingState = "ENABLED" - ds.LastConnected = 5 - ds.LastReported = 5 - ds.Description = name - - var err error - ds.Addressable.Id, err = db.AddAddressable(ds.Addressable) - if err != nil { - return ds, fmt.Errorf("Error creating addressable: %v", err) - } - - return ds, nil -} - -func getCommand(db interfaces.DBClient, i int) models.Command { - name := fmt.Sprintf("name%d", i) - c := models.Command{} - c.Name = name - c.Put = models.Put{} - c.Get = models.Get{} - return c -} - -func getDeviceProfile(db interfaces.DBClient, i int) (models.DeviceProfile, error) { - name := fmt.Sprintf("name%d", i) - dp := models.DeviceProfile{} - dp.Name = name - dp.Manufacturer = name - dp.Model = name - dp.Labels = append(dp.Labels, name) - // TODO - // dp.DeviceResources = append(dp.DeviceResources, name) - // dp.Resources = append(dp.Resources, name) - c := getCommand(db, i) - dp.CoreCommands = append(dp.CoreCommands, c) - return dp, nil -} - -func populateAddressable(db interfaces.DBClient, count int) (string, error) { - var id string - for i := 0; i < count; i++ { - var err error - a := getAddressable(i, "") - id, err = db.AddAddressable(a) - if err != nil { - return id, err - } - } - return id, nil -} - -func populateDeviceService(db interfaces.DBClient, count int) (string, error) { - var id string - for i := 0; i < count; i++ { - ds, err := getDeviceService(db, i) - if err != nil { - return id, nil - } - id, err = db.AddDeviceService(ds) - if err != nil { - return id, fmt.Errorf("Error creating device service: %v", err) - } - } - return id, nil -} - -func populateDeviceReport(db interfaces.DBClient, count int) (string, error) { - var id string - for i := 0; i < count; i++ { - var err error - name := fmt.Sprintf("name%d", i) - dr := models.DeviceReport{} - dr.Name = name - dr.Device = name - dr.Action = name - dr.Expected = append(dr.Expected, name) - id, err = db.AddDeviceReport(dr) - if err != nil { - return id, err - } - } - return id, nil -} - -func populateDevice(db interfaces.DBClient, count int) (string, error) { - var id string - for i := 0; i < count; i++ { - var err error - name := fmt.Sprintf("name%d", i) - d := models.Device{} - d.Name = name - d.AdminState = "UNLOCKED" - d.OperatingState = "ENABLED" - d.LastConnected = 4 - d.LastReported = 4 - d.Labels = append(d.Labels, name) - - d.Protocols = getProtocols() - d.Service, err = getDeviceService(db, i) - if err != nil { - return id, nil - } - d.Service.Id, err = db.AddDeviceService(d.Service) - if err != nil { - return id, fmt.Errorf("Error creating DeviceService: %v", err) - } - - d.Profile, err = getDeviceProfile(db, i) - if err != nil { - return id, fmt.Errorf("Error getting DeviceProfile: %v", err) - } - d.Profile.Id, err = db.AddDeviceProfile(d.Profile) - if err != nil { - return id, fmt.Errorf("Error creating DeviceProfile: %v", err) - } - - id, err = db.AddDevice(d, d.Profile.CoreCommands) - if err != nil { - return id, err - } - } - return id, nil -} - -func populateDeviceProfile(db interfaces.DBClient, count int) (string, error) { - var id string - for i := 0; i < count; i++ { - dp, err := getDeviceProfile(db, i) - if err != nil { - return id, fmt.Errorf("Error getting DeviceProfile: %v", err) - } - id, err = db.AddDeviceProfile(dp) - if err != nil { - return id, err - } - } - return id, nil -} - -func populateProvisionWatcher(db interfaces.DBClient, count int) (string, error) { - var id string - for i := 0; i < count; i++ { - var err error - name := fmt.Sprintf("name%d", i) - d := models.ProvisionWatcher{} - d.Name = name - d.OperatingState = "ENABLED" - d.Identifiers = make(map[string]string) - d.Identifiers["name"] = name - - d.Service, err = getDeviceService(db, i) - if err != nil { - return id, err - } - d.Service.Id, err = db.AddDeviceService(d.Service) - if err != nil { - return id, fmt.Errorf("Error creating DeviceService: %v", err) - } - d.Profile, err = getDeviceProfile(db, i) - if err != nil { - return id, fmt.Errorf("Error getting DeviceProfile: %v", err) - } - d.Profile.Id, err = db.AddDeviceProfile(d.Profile) - if err != nil { - return id, fmt.Errorf("Error creating DeviceProfile: %v", err) - } - id, err = db.AddProvisionWatcher(d) - if err != nil { - return id, err - } - } - return id, nil -} - -func clearAddressables(t *testing.T, db interfaces.DBClient) { - addrs, err := db.GetAddressables() - if err != nil { - t.Fatalf("Error getting addressables %v", err) - } - for _, a := range addrs { - if err = db.DeleteAddressableById(a.Id); err != nil { - t.Fatalf("Error removing addressable %v: %v", a, err) - } - } -} - -func clearDevices(t *testing.T, db interfaces.DBClient) { - ds, err := db.GetAllDevices() - if err != nil { - t.Fatalf("Error getting devices %v", err) - } - for _, d := range ds { - if err = db.DeleteDeviceById(d.Id); err != nil { - t.Fatalf("Error removing device %v: %v", d, err) - } - } -} - -func clearDeviceServices(t *testing.T, db interfaces.DBClient) { - dss, err := db.GetAllDeviceServices() - if err != nil { - t.Fatalf("Error getting deviceServices %v", err) - } - for _, ds := range dss { - if err = db.DeleteDeviceServiceById(ds.Id); err != nil { - t.Fatalf("Error removing deviceService %v: %v", ds, err) - } - } -} - -func clearDeviceReports(t *testing.T, db interfaces.DBClient) { - drs, err := db.GetAllDeviceReports() - if err != nil { - t.Fatalf("Error getting deviceReports %v", err) - } - for _, ds := range drs { - if err = db.DeleteDeviceReportById(ds.Id); err != nil { - t.Fatalf("Error removing deviceReport %v: %v", ds, err) - } - } -} - -func clearDeviceProfiles(t *testing.T, db interfaces.DBClient) { - var dps []models.DeviceProfile - dps, err := db.GetAllDeviceProfiles() - if err != nil { - t.Fatalf("Error getting deviceProfiles %v", err) - } - - for _, ds := range dps { - if err = db.DeleteDeviceProfileById(ds.Id); err != nil { - t.Fatalf("Error removing deviceProfile %v: %v", ds, err) - } - } -} - -func testDBAddressables(t *testing.T, db interfaces.DBClient) { - var addrs []models.Addressable - - clearAddressables(t, db) - - id, err := populateAddressable(db, 100) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - - // Error to have an Addressable with the same name - _, err = populateAddressable(db, 1) - if err == nil { - t.Fatalf("Should not be able to add a duplicated addressable\n") - } - - addrs, err = db.GetAddressables() - if err != nil { - t.Fatalf("Error getting addressables %v", err) - } - if len(addrs) != 100 { - t.Fatalf("There should be 100 addressables instead of %d", len(addrs)) - } - a := models.Addressable{} - a, err = db.GetAddressableById(id) - if err != nil { - t.Fatalf("Error getting addressable by id %v", err) - } - if a.Id != id { - t.Fatalf("Id does not match %s - %s", a.Id, id) - } - _, err = db.GetAddressableById("INVALID") - if err == nil { - t.Fatalf("Addressable should not be found") - } - a, err = db.GetAddressableByName("name1") - if err != nil { - t.Fatalf("Error getting addressable by name %v", err) - } - if a.Name != "name1" { - t.Fatalf("name does not match %s - %s", a.Name, "name1") - } - _, err = db.GetAddressableByName("INVALID") - if err == nil { - t.Fatalf("Addressable should not be found") - } - - addrs, err = db.GetAddressablesByTopic("name1") - if err != nil { - t.Fatalf("Error getting addressables by topic: %v", err) - } - if len(addrs) != 1 { - t.Fatalf("There should be 1 addressable, not %d", len(addrs)) - } - - addrs, err = db.GetAddressablesByTopic("INVALID") - if err != nil { - t.Fatalf("Error getting addressables by topic: %v", err) - } - if len(addrs) != 0 { - t.Fatalf("There should be no addressables, not %d", len(addrs)) - } - - addrs, err = db.GetAddressablesByPort(2) - if err != nil { - t.Fatalf("Error getting addressables by port: %v", err) - } - if len(addrs) != 1 { - t.Fatalf("There should be 1 addressable, not %d", len(addrs)) - } - - addrs, err = db.GetAddressablesByPort(-1) - if err != nil { - t.Fatalf("Error getting addressables by port: %v", err) - } - if len(addrs) != 0 { - t.Fatalf("There should be no addressables, not %d", len(addrs)) - } - - addrs, err = db.GetAddressablesByPublisher("name1") - if err != nil { - t.Fatalf("Error getting addressables by publisher: %v", err) - } - if len(addrs) != 1 { - t.Fatalf("There should be 1 addressable, not %d", len(addrs)) - } - - addrs, err = db.GetAddressablesByPublisher("INVALID") - if err != nil { - t.Fatalf("Error getting addressables by publisher: %v", err) - } - if len(addrs) != 0 { - t.Fatalf("There should be no addressables, not %d", len(addrs)) - } - - addrs, err = db.GetAddressablesByAddress("name1") - if err != nil { - t.Fatalf("Error getting addressables by Address: %v", err) - } - if len(addrs) != 1 { - t.Fatalf("There should be 1 addressable, not %d", len(addrs)) - } - - addrs, err = db.GetAddressablesByAddress("INVALID") - if err != nil { - t.Fatalf("Error getting addressables by Address: %v", err) - } - if len(addrs) != 0 { - t.Fatalf("There should be no addressables, not %d", len(addrs)) - } - - a, err = db.GetAddressableById(id) - if err != nil { - t.Fatalf("Error getting addressable %v", err) - } - a, err = db.GetAddressableByName("name1") - if err != nil { - t.Fatalf("Error getting addressable %v", err) - } - - a.Name = "name" - err = db.UpdateAddressable(a) - if err != nil { - t.Fatalf("Error updating Addressable %v", err) - } - a, err = db.GetAddressableByName("name1") - if err == nil { - t.Fatalf("Addressable name1 should be renamed") - } - a, err = db.GetAddressableByName("name") - if err != nil { - t.Fatalf("Addressable name should be renamed") - } - - // aa.Name = "name2" - // err = db.UpdateAddressable(&aa, &a) - // if err == nil { - // t.Fatalf("Error updating Addressable %v", err) - // } - - a.Id = "INVALID" - a.Name = "INVALID" - err = db.DeleteAddressableById(a.Id) - if err == nil { - t.Fatalf("Addressable should not be deleted") - } - - a, err = db.GetAddressableByName("name") - if err != nil { - t.Fatalf("Error getting addressable") - } - err = db.DeleteAddressableById(a.Id) - if err != nil { - t.Fatalf("Addressable should be deleted: %v", err) - } - - clearAddressables(t, db) -} - -func testDBCommand(t *testing.T, db interfaces.DBClient) { - var commands []models.Command - - clearDevices(t, db) - clearDeviceServices(t, db) - clearDeviceProfiles(t, db) - clearAddressables(t, db) - did, err := populateDevice(db, 100) - - commands, err = db.GetAllCommands() - if err != nil { - t.Fatalf("Error getting commands %v", err) - } - if len(commands) != 100 { - t.Fatalf("There should be 100 commands instead of %d", len(commands)) - } - - id := commands[0].Id - c, err := db.GetCommandById(id) - if err != nil { - t.Fatalf("Error getting command by id %v", err) - } - if c.Id != id { - t.Fatalf("Id does not match %s - %s", c.Id, id) - } - _, err = db.GetCommandById("INVALID") - if err == nil { - t.Fatalf("Command should not be found") - } - - commands, err = db.GetCommandsByName("name1") - if err != nil { - t.Fatalf("Error getting commands by name %v", err) - } - if len(commands) != 1 { - t.Fatalf("There should be 1 commands instead of %d", len(commands)) - } - - commands, err = db.GetCommandsByName("INVALID") - if err != nil { - t.Fatalf("Error getting commands by name %v", err) - } - if len(commands) != 0 { - t.Fatalf("There should be 1 commands instead of %d", len(commands)) - } - - commands, err = db.GetCommandsByDeviceId(did) - if err != nil { - t.Fatalf("Error getting commands by device id %v", err) - } - if len(commands) != 1 { - t.Fatalf("There should be 1 commands instead of %d", len(commands)) - } - - cn := commands[0].Name - command, err := db.GetCommandByNameAndDeviceId(cn, did) - if err != nil { - t.Fatalf("Error getting commands by name and device id: %v", err) - } - if command.Name != commands[0].Name { - t.Fatalf("Name does not match %s - %s", command.Name, cn) - } - - _, err = db.GetCommandsByDeviceId(uuid.New().String()) - require.IsType(t, err, errors.ErrItemNotFound{}) - - err = db.DeleteDeviceById(did) - if err != nil { - t.Fatalf("Error removing device by id %v", err) - } - - commands, err = db.GetAllCommands() - if err != nil { - t.Fatalf("Error getting commands %v", err) - } - if len(commands) != 99 { - t.Fatalf("There should be 100 commands instead of %d", len(commands)) - } -} - -func testDBDeviceService(t *testing.T, db interfaces.DBClient) { - var deviceServices []models.DeviceService - - clearDeviceServices(t, db) - clearAddressables(t, db) - - id, err := populateDeviceService(db, 100) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - - ds2 := models.DeviceService{} - ds2.Name = "name1" - _, err = db.AddDeviceService(ds2) - if err == nil { - t.Fatalf("Should be an error adding an existing name") - } - - deviceServices, err = db.GetAllDeviceServices() - if err != nil { - t.Fatalf("Error getting deviceServices %v", err) - } - if len(deviceServices) != 100 { - t.Fatalf("There should be 100 deviceServices instead of %d", len(deviceServices)) - } - ds, err := db.GetDeviceServiceById(id) - if err != nil { - t.Fatalf("Error getting deviceService by id %v", err) - } - if ds.Id != id { - t.Fatalf("Id does not match %s - %s", ds.Id, id) - } - _, err = db.GetDeviceServiceById("INVALID") - if err == nil { - t.Fatalf("DeviceService should not be found") - } - - ds, err = db.GetDeviceServiceByName("name1") - if err != nil { - t.Fatalf("Error getting deviceServices by name %v", err) - } - if ds.Name != "name1" { - t.Fatalf("The ds should be named name1 instead of %s", ds.Name) - } - - _, err = db.GetDeviceServiceByName("INVALID") - if err == nil { - t.Fatalf("There should be a not found error") - } - - deviceServices, err = db.GetDeviceServicesByAddressableId(ds.Addressable.Id) - if err != nil { - t.Fatalf("Error getting deviceServices by addressable id %v", err) - } - if len(deviceServices) != 1 { - t.Fatalf("There should be 1 deviceServices instead of %d", len(deviceServices)) - } - deviceServices, err = db.GetDeviceServicesByAddressableId(uuid.New().String()) - if err != dataBase.ErrNotFound && len(deviceServices) != 0 { // XXX Inconsistent use of ErrNotFound - t.Fatalf("Error getting deviceServices by addressable id") - } - - deviceServices, err = db.GetDeviceServicesWithLabel("name3") - if err != nil { - t.Fatalf("Error getting deviceServices by addressable id %v", err) - } - if len(deviceServices) != 1 { - t.Fatalf("There should be 1 deviceServices instead of %d", len(deviceServices)) - } - deviceServices, err = db.GetDeviceServicesWithLabel("INVALID") - if err != nil { - t.Fatalf("Error getting deviceServices by addressable id %v", err) - } - if len(deviceServices) != 0 { - t.Fatalf("There should be 0 deviceServices instead of %d", len(deviceServices)) - } - - ds.Id = id - ds.Name = "name" - err = db.UpdateDeviceService(ds) - if err != nil { - t.Fatalf("Error updating DeviceService %v", err) - } - - ds.Id = "INVALID" - err = db.UpdateDeviceService(ds) - if err == nil { - t.Fatalf("Should return error") - } - - err = db.DeleteDeviceServiceById(ds.Id) - if err == nil { - t.Fatalf("DeviceService should not be deleted") - } - - ds.Id = id - err = db.DeleteDeviceServiceById(ds.Id) - if err != nil { - t.Fatalf("DeviceService should be deleted: %v", err) - } - - clearDeviceServices(t, db) -} - -func testDBDeviceReport(t *testing.T, db interfaces.DBClient) { - var deviceReports []models.DeviceReport - - clearDeviceReports(t, db) - - id, err := populateDeviceReport(db, 100) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - - e := models.DeviceReport{} - e.Name = "name1" - _, err = db.AddDeviceReport(e) - if err == nil { - t.Fatalf("Should be an error adding an existing name") - } - - deviceReports, err = db.GetAllDeviceReports() - if err != nil { - t.Fatalf("Error getting deviceReports %v", err) - } - if len(deviceReports) != 100 { - t.Fatalf("There should be 100 deviceReports instead of %d", len(deviceReports)) - } - - e, err = db.GetDeviceReportById(id) - if err != nil { - t.Fatalf("Error getting deviceReport by id %v", err) - } - if e.Id != id { - t.Fatalf("Id does not match %s - %s", e.Id, id) - } - _, err = db.GetDeviceReportById("INVALID") - if err == nil { - t.Fatalf("DeviceReport should not be found") - } - - e, err = db.GetDeviceReportByName("name1") - if err != nil { - t.Fatalf("Error getting deviceReport by id %v", err) - } - if e.Name != "name1" { - t.Fatalf("Id does not match %s - %s", e.Id, id) - } - _, err = db.GetDeviceReportByName("INVALID") - if err == nil { - t.Fatalf("DeviceReport should not be found") - } - - deviceReports, err = db.GetDeviceReportByDeviceName("name1") - if err != nil { - t.Fatalf("Error getting deviceReports %v", err) - } - if len(deviceReports) != 1 { - t.Fatalf("There should be 1 deviceReports instead of %d", len(deviceReports)) - } - - deviceReports, err = db.GetDeviceReportByDeviceName("name") - if err != nil { - t.Fatalf("Error getting deviceReports %v", err) - } - if len(deviceReports) != 0 { - t.Fatalf("There should be 0 deviceReports instead of %d", len(deviceReports)) - } - - deviceReports, err = db.GetDeviceReportsByAction("name1") - if err != nil { - t.Fatalf("Error getting deviceReports %v", err) - } - if len(deviceReports) != 1 { - t.Fatalf("There should be 1 deviceReports instead of %d", len(deviceReports)) - } - - deviceReports, err = db.GetDeviceReportsByAction("name") - if err != nil { - t.Fatalf("Error getting deviceReports %v", err) - } - if len(deviceReports) != 0 { - t.Fatalf("There should be 0 deviceReports instead of %d", len(deviceReports)) - } - - e2 := models.DeviceReport{} - e2.Id = id - e2.Name = "name" - err = db.UpdateDeviceReport(e2) - if err != nil { - t.Fatalf("Error updating DeviceReport %v", err) - } - - e2.Id = "INVALID" - err = db.UpdateDeviceReport(e2) - if err == nil { - t.Fatalf("Should return error") - } - - err = db.DeleteDeviceReportById(e2.Id) - if err == nil { - t.Fatalf("DeviceReport should not be deleted") - } - - e2.Id = id - err = db.DeleteDeviceReportById(e2.Id) - if err != nil { - t.Fatalf("DeviceReport should be deleted: %v", err) - } -} - -func testDBDeviceProfile(t *testing.T, db interfaces.DBClient) { - var deviceProfiles []models.DeviceProfile - - clearAddressables(t, db) - clearDeviceProfiles(t, db) - id, err := populateDeviceProfile(db, 100) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - - dp := models.DeviceProfile{} - dp.Name = "name1" - _, err = db.AddDeviceProfile(dp) - if err == nil { - t.Fatalf("Should be an error adding an existing name") - } - - deviceProfiles, err = db.GetAllDeviceProfiles() - if err != nil { - t.Fatalf("Error getting deviceProfiles %v", err) - } - if len(deviceProfiles) != 100 { - t.Fatalf("There should be 100 deviceProfiles instead of %d", len(deviceProfiles)) - } - - dp, err = db.GetDeviceProfileById(id) - if err != nil { - t.Fatalf("Error getting deviceProfile by id %v", err) - } - if dp.Id != id { - t.Fatalf("Id does not match %s - %s", dp.Id, id) - } - _, err = db.GetDeviceProfileById("INVALID") - if err == nil { - t.Fatalf("DeviceProfile should not be found") - } - - dp, err = db.GetDeviceProfileByName("name1") - if err != nil { - t.Fatalf("Error getting deviceProfile by id %v", err) - } - if dp.Name != "name1" { - t.Fatalf("Id does not match %s - %s", dp.Id, id) - } - _, err = db.GetDeviceProfileByName("INVALID") - if err == nil { - t.Fatalf("DeviceProfile should not be found") - } - - deviceProfiles, err = db.GetDeviceProfilesByModel("name1") - if err != nil { - t.Fatalf("Error getting deviceProfiles %v", err) - } - if len(deviceProfiles) != 1 { - t.Fatalf("There should be 1 deviceProfiles instead of %d", len(deviceProfiles)) - } - - deviceProfiles, err = db.GetDeviceProfilesByModel("name") - if err != nil { - t.Fatalf("Error getting deviceProfiles %v", err) - } - if len(deviceProfiles) != 0 { - t.Fatalf("There should be 0 deviceProfiles instead of %d", len(deviceProfiles)) - } - - deviceProfiles, err = db.GetDeviceProfilesByManufacturer("name1") - if err != nil { - t.Fatalf("Error getting deviceProfiles %v", err) - } - if len(deviceProfiles) != 1 { - t.Fatalf("There should be 1 deviceProfiles instead of %d", len(deviceProfiles)) - } - - deviceProfiles, err = db.GetDeviceProfilesByManufacturer("name") - if err != nil { - t.Fatalf("Error getting deviceProfiles %v", err) - } - if len(deviceProfiles) != 0 { - t.Fatalf("There should be 0 deviceProfiles instead of %d", len(deviceProfiles)) - } - - deviceProfiles, err = db.GetDeviceProfilesByManufacturerModel("name1", "name1") - if err != nil { - t.Fatalf("Error getting deviceProfiles %v", err) - } - if len(deviceProfiles) != 1 { - t.Fatalf("There should be 1 deviceProfiles instead of %d", len(deviceProfiles)) - } - - deviceProfiles, err = db.GetDeviceProfilesByManufacturerModel("name", "name1") - if err != nil { - t.Fatalf("Error getting deviceProfiles %v", err) - } - if len(deviceProfiles) != 0 { - t.Fatalf("There should be 0 deviceProfiles instead of %d", len(deviceProfiles)) - } - - deviceProfiles, err = db.GetDeviceProfilesWithLabel("name1") - if err != nil { - t.Fatalf("Error getting deviceProfiles %v", err) - } - if len(deviceProfiles) != 1 { - t.Fatalf("There should be 1 deviceProfiles instead of %d", len(deviceProfiles)) - } - - deviceProfiles, err = db.GetDeviceProfilesWithLabel("name") - if err != nil { - t.Fatalf("Error getting deviceProfiles %v", err) - } - if len(deviceProfiles) != 0 { - t.Fatalf("There should be 0 deviceProfiles instead of %d", len(deviceProfiles)) - } - d2 := models.DeviceProfile{} - d2.Id = id - d2.Name = "name" - err = db.UpdateDeviceProfile(d2) - if err != nil { - t.Fatalf("Error updating DeviceProfile %v", err) - } - - d2.Id = "INVALID" - err = db.UpdateDeviceProfile(d2) - if err == nil { - t.Fatalf("Should return error") - } - - err = db.DeleteDeviceProfileById(d2.Id) - if err == nil { - t.Fatalf("DeviceProfile should not be deleted") - } - - d2.Id = id - err = db.DeleteDeviceProfileById(d2.Id) - if err != nil { - t.Fatalf("DeviceProfile should be deleted: %v", err) - } - - clearDeviceProfiles(t, db) -} - -func testDBDevice(t *testing.T, db interfaces.DBClient) { - var devices []models.Device - - clearDevices(t, db) - clearDeviceServices(t, db) - clearDeviceProfiles(t, db) - clearAddressables(t, db) - id, err := populateDevice(db, 100) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - - d := models.Device{} - d.Name = "name1" - _, err = db.AddDevice(d, d.Profile.CoreCommands) - if err == nil { - t.Fatalf("Should be an error adding an existing name") - } - - devices, err = db.GetAllDevices() - if err != nil { - t.Fatalf("Error getting devices %v", err) - } - if len(devices) != 100 { - t.Fatalf("There should be 100 devices instead of %d", len(devices)) - } - - d, err = db.GetDeviceById(id) - if err != nil { - t.Fatalf("Error getting device by id %v", err) - } - if d.Id != id { - t.Fatalf("Id does not match %s - %s", d.Id, id) - } - _, err = db.GetDeviceById("INVALID") - if err == nil { - t.Fatalf("Device should not be found") - } - - d, err = db.GetDeviceByName("name1") - if err != nil { - t.Fatalf("Error getting device by id %v", err) - } - if d.Name != "name1" { - t.Fatalf("Id does not match %s - %s", d.Id, id) - } - _, err = db.GetDeviceByName("INVALID") - if err == nil { - t.Fatalf("Device should not be found") - } - - devices, err = db.GetDevicesByProfileId(d.Profile.Id) - if err != nil { - t.Fatalf("Error getting devices %v", err) - } - if len(devices) != 1 { - t.Fatalf("There should be 1 devices instead of %d", len(devices)) - } - - devices, err = db.GetDevicesByProfileId(uuid.New().String()) - if (err != nil && err != dataBase.ErrNotFound) || len(devices) != 0 { - t.Fatalf("Error getting devices %v", err) - } - if len(devices) != 0 { - t.Fatalf("There should be 0 devices instead of %d", len(devices)) - } - - devices, err = db.GetDevicesByServiceId(d.Service.Id) - if err != nil { - t.Fatalf("Error getting devices %v", err) - } - if len(devices) != 1 { - t.Fatalf("There should be 1 devices instead of %d", len(devices)) - } - - devices, err = db.GetDevicesByServiceId(uuid.New().String()) - if (err != nil && err != dataBase.ErrNotFound) || len(devices) != 0 { - t.Fatalf("Error getting devices %v", err) - } - if len(devices) != 0 { - t.Fatalf("There should be 0 devices instead of %d", len(devices)) - } - - devices, err = db.GetDevicesWithLabel("name1") - if err != nil { - t.Fatalf("Error getting devices %v", err) - } - if len(devices) != 1 { - t.Fatalf("There should be 1 devices instead of %d", len(devices)) - } - - devices, err = db.GetDevicesWithLabel("name") - if err != nil { - t.Fatalf("Error getting devices %v", err) - } - if len(devices) != 0 { - t.Fatalf("There should be 0 devices instead of %d", len(devices)) - } - - d.Id = id - d.Name = "name" - err = db.UpdateDevice(d) - if err != nil { - t.Fatalf("Error updating Device %v", err) - } - - d.Id = "INVALID" - err = db.UpdateDevice(d) - if err == nil { - t.Fatalf("Should return error") - } - - err = db.DeleteDeviceById(d.Id) - if err == nil { - t.Fatalf("Device should not be deleted") - } - - d.Id = id - err = db.DeleteDeviceById(d.Id) - if err != nil { - t.Fatalf("Device should be deleted: %v", err) - } -} - -func testDBProvisionWatcher(t *testing.T, db interfaces.DBClient) { - var provisionWatchers []models.ProvisionWatcher - - clearDeviceProfiles(t, db) - clearDeviceServices(t, db) - clearAddressables(t, db) - id, err := populateProvisionWatcher(db, 100) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - - pw := models.ProvisionWatcher{} - pw.Name = "name1" - _, err = db.AddProvisionWatcher(pw) - if err == nil { - t.Fatalf("Should be an error adding an existing name") - } - - provisionWatchers, err = db.GetAllProvisionWatchers() - if err != nil { - t.Fatalf("Error getting provisionWatchers %v", err) - } - if len(provisionWatchers) != 100 { - t.Fatalf("There should be 100 provisionWatchers instead of %d", len(provisionWatchers)) - } - - pw, err = db.GetProvisionWatcherById(id) - if err != nil { - t.Fatalf("Error getting provisionWatcher by id %v", err) - } - if pw.Id != id { - t.Fatalf("Id does not match %s - %s", pw.Id, id) - } - _, err = db.GetProvisionWatcherById("INVALID") - if err == nil { - t.Fatalf("ProvisionWatcher should not be found") - } - - pw, err = db.GetProvisionWatcherByName("name1") - if err != nil { - t.Fatalf("Error getting provisionWatcher by id %v", err) - } - if pw.Name != "name1" { - t.Fatalf("Id does not match %s - %s", pw.Id, id) - } - _, err = db.GetProvisionWatcherByName("INVALID") - if err == nil { - t.Fatalf("ProvisionWatcher should not be found") - } - - provisionWatchers, err = db.GetProvisionWatchersByServiceId(pw.Service.Id) - if err != nil { - t.Fatalf("Error getting provisionWatchers %v", err) - } - if len(provisionWatchers) != 1 { - t.Fatalf("There should be 1 provisionWatchers instead of %d", len(provisionWatchers)) - } - - provisionWatchers, err = db.GetProvisionWatchersByServiceId(uuid.New().String()) - if (err != nil && err != dataBase.ErrNotFound) || len(provisionWatchers) != 0 { - t.Fatalf("Error getting provisionWatchers %v", err) - } - if len(provisionWatchers) != 0 { - t.Fatalf("There should be 0 provisionWatchers instead of %d", len(provisionWatchers)) - } - - provisionWatchers, err = db.GetProvisionWatchersByProfileId(pw.Profile.Id) - if err != nil { - t.Fatalf("Error getting provisionWatchers %v", err) - } - if len(provisionWatchers) != 1 { - t.Fatalf("There should be 1 provisionWatchers instead of %d", len(provisionWatchers)) - } - - provisionWatchers, err = db.GetProvisionWatchersByProfileId(uuid.New().String()) - if (err != nil && err != dataBase.ErrNotFound) || len(provisionWatchers) != 0 { - t.Fatalf("Error getting provisionWatchers %v", err) - } - if len(provisionWatchers) != 0 { - t.Fatalf("There should be 0 provisionWatchers instead of %d", len(provisionWatchers)) - } - - provisionWatchers, err = db.GetProvisionWatchersByIdentifier("name", "name1") - if err != nil { - t.Fatalf("Error getting provisionWatchers %v", err) - } - if len(provisionWatchers) != 1 { - t.Fatalf("There should be 1 provisionWatchers instead of %d", len(provisionWatchers)) - } - - provisionWatchers, err = db.GetProvisionWatchersByIdentifier("name", "invalid") - if err != nil { - t.Fatalf("Error getting provisionWatchers %v", err) - } - if len(provisionWatchers) != 0 { - t.Fatalf("There should be 0 provisionWatchers instead of %d", len(provisionWatchers)) - } - - pw.Name = "name" - err = db.UpdateProvisionWatcher(pw) - if err != nil { - t.Fatalf("Error updating ProvisionWatcher %v", err) - } - - pw.Id = "INVALID" - err = db.UpdateProvisionWatcher(pw) - if err == nil { - t.Fatalf("Should return error") - } - - err = db.DeleteProvisionWatcherById(pw.Id) - if err == nil { - t.Fatalf("ProvisionWatcher should not be deleted") - } - - pw.Id = id - err = db.DeleteProvisionWatcherById(pw.Id) - if err != nil { - t.Fatalf("ProvisionWatcher should be deleted: %v", err) - } -} diff --git a/internal/pkg/db/test/db_notifications.go b/internal/pkg/db/test/db_notifications.go deleted file mode 100644 index cb983d8ebf..0000000000 --- a/internal/pkg/db/test/db_notifications.go +++ /dev/null @@ -1,416 +0,0 @@ -// -// Copyright (C) 2018 IOTech Ltd -// -// SPDX-License-Identifier: Apache-2.0 -// - -package test - -import ( - "fmt" - "testing" - - dbp "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/interfaces" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -func TestNotificationsDB(t *testing.T, db interfaces.DBClient) { - // Remove previous notification and transmission - err := db.Cleanup() - if err != nil { - t.Fatalf("Error clean db: %v\n", err) - } - cleanUpAllSubscription(db) - - testDBNotification(t, db) - testDBSubscription(t, db) - testDBTransmission(t, db) - - defer db.CloseSession() - // Calling CloseSession twice to test that there is no panic when closing an - // already closed db - defer db.CloseSession() -} - -func testDBNotification(t *testing.T, db interfaces.DBClient) { - // Prepare test data - beforeTime := dbp.MakeTimestamp() - err := populateNotification(db, 0, 10, contract.New) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - err = populateNotification(db, 10, 20, contract.Escalated) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - err = populateNotification(db, 20, 30, contract.Processed) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - afterTime := dbp.MakeTimestamp() - - // Test GetNotifications - notifications, err := db.GetNotifications() - if err != nil { - t.Fatalf("Error getting notifications %v", err) - } - if len(notifications) != 30 { - t.Fatalf("There should be 30 notifications instead of %d", len(notifications)) - } - - // Test GetNotificationById - slugName := "slug-test" - notification := getNotification(slugName, contract.New) - notification.ID, err = db.AddNotification(notification) - if err != nil { - t.Fatalf("Fail to add notification") - } - n, err := db.GetNotificationById(notification.ID) - if err != nil { - t.Fatalf("Fail to getting notification by id %v", err) - } - if n.ID != notification.ID { - t.Fatalf("ID does not match %s - %s", n.ID, notification.ID) - } - - // Test GetNotificationBySlug - n, err = db.GetNotificationBySlug(slugName) - if err != nil { - t.Fatalf("Error getting notification by slug %v", err) - } - if n.Slug != slugName { - t.Fatalf("Slug does not match %s - %s", n.Slug, slugName) - } - - // Test GetNotificationBySender - sender := "test-sender" - notifications, err = db.GetNotificationBySender(sender, 5) - if err != nil { - t.Fatalf("Error getting notifications by sender: %v", err) - } - if len(notifications) == 0 { - t.Fatalf("There should be at least one notification") - } - if notifications[0].Sender != sender { - t.Fatalf("Sender does not match %s - %s", n.Sender, sender) - } - - // Test GetNotificationsByLabels - labels := []string{"labelA", "labelB"} - notifications, err = db.GetNotificationsByLabels(labels, 5) - if err != nil { - t.Fatalf("Error getting notifications %v", err) - } - if len(notifications) == 0 { - t.Fatalf("There should be at least one notification") - } - - // Test GetNotificationsByStartEnd - notifications, err = db.GetNotificationsByStartEnd(beforeTime, afterTime, 5) - if err != nil { - t.Fatalf("Error getting notifications %v", err) - } - if len(notifications) != 5 { - t.Fatalf("There should be five notifications") - } - - // Test GetNotificationsByStart - notifications, err = db.GetNotificationsByStart(beforeTime, 5) - if err != nil { - t.Fatalf("Error getting notifications %v", err) - } - if len(notifications) != 5 { - t.Fatalf("There should be five notifications") - } - - // Test GetNotificationsByEnd - notifications, err = db.GetNotificationsByEnd(afterTime, 5) - if err != nil { - t.Fatalf("Error getting notifications %v", err) - } - if len(notifications) != 5 { - t.Fatalf("There should be five notifications") - } - - // Test GetNewNotifications - notifications, err = db.GetNewNotifications(5) - if err != nil { - t.Fatalf("Error getting notifications %v", err) - } - if len(notifications) != 5 { - t.Fatalf("There should be five notifications") - } - - // Test GetNewNormalNotifications - notifications, err = db.GetNewNormalNotifications(5) - if err != nil { - t.Fatalf("Error getting notifications %v", err) - } - if len(notifications) != 5 { - t.Fatalf("There should be five notifications") - } - - // Test MarkNotificationProcessed - err = db.MarkNotificationProcessed(notification) - if err != nil { - t.Fatalf("Fail to mark notification to processed status, %v", err) - } - n, err = db.GetNotificationBySlug(slugName) - if err != nil { - t.Fatalf("Error getting notification by slug %v", err) - } - if n.Status != contract.Processed { - t.Fatalf("Notification status should be %v ", contract.Processed) - } - - // Test DeleteNotificationBySlug - err = db.DeleteNotificationBySlug(notification.Slug) - if err != nil { - t.Fatalf("Fail to delete notification by slug '%v'", notification.Slug) - } - - // Test DeleteNotificationsOld - err = db.DeleteNotificationsOld(0) - if err != nil { - t.Fatalf("Fail to delete old notifications, '%v'", err) - } - -} - -func testDBSubscription(t *testing.T, db interfaces.DBClient) { - // Prepare test data - err := populateSubscription(db, 30) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - - // Test GetSubscriptions - subscriptions, err := db.GetSubscriptions() - if err != nil { - t.Fatalf("Error getting notifications %v", err) - } - if len(subscriptions) != 30 { - t.Fatalf("There should be 30 notifications instead of %d", len(subscriptions)) - } - - // Test GetSubscriptionById - slugName := "slug-test" - subscription := getSubscription(slugName) - subscription.ID, err = db.AddSubscription(subscription) - if err != nil { - t.Fatalf("Fail to add subscription, %v", err) - } - s, err := db.GetSubscriptionById(subscription.ID) - if err != nil { - t.Fatalf("Fail to get subscription by ID, %v", err) - } - if s.Slug != slugName { - t.Fatalf("Unexpect test result, slug '%v' not match '%v'", s.Slug, slugName) - } - - // Test GetSubscriptionBySlug - s, err = db.GetSubscriptionBySlug(slugName) - if err != nil { - t.Fatalf("Fail to get subscription by slug, %v", err) - } - if s.Slug != slugName { - t.Fatalf("Unexpect test result, slug '%v' not match '%v'", s.Slug, slugName) - } - - // Test GetSubscriptionByReceiver - receiverName := "test-receiver" - subscriptions, err = db.GetSubscriptionByReceiver(receiverName) - if err != nil { - t.Fatalf("Fail to get subscription by receiver, %v", err) - } - if subscriptions[0].Receiver != receiverName { - t.Fatalf("Unexpect test result, receiver '%v' not match '%v'", subscriptions[0].Receiver, receiverName) - } - - // Test GetSubscriptionByCategories - categories := []string{contract.Security, contract.Hwhealth} - _, err = db.GetSubscriptionByCategories(categories) - if err != nil { - t.Fatalf("Fail to get subscription by categories, %v", err) - } - - // Test GetSubscriptionByLabels - labels := []string{"labelA", "labelB"} - _, err = db.GetSubscriptionByLabels(labels) - if err != nil { - t.Fatalf("Fail to get subscription by labels, %v", err) - } - - // Test GetSubscriptionByCategoriesLabels - categories = []string{contract.Hwhealth, contract.Swhealth} - labels = []string{"labelA"} - _, err = db.GetSubscriptionByCategoriesLabels(categories, labels) - if err != nil { - t.Fatalf("Fail to get subscription by categories and labels, %v", err) - } -} - -func testDBTransmission(t *testing.T, db interfaces.DBClient) { - // Prepare test data - err := populateTransmission(db, 30, 10) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - - // Test UpdateTransmission - slugName := "slug-test" - transmission := getTransmission(slugName, 10) - transmission.ID, err = db.AddTransmission(transmission) - if err != nil { - t.Fatalf("Fail to add subscription, %v", err) - } - transmission.Status = contract.Failed - err = db.UpdateTransmission(transmission) - if err != nil { - t.Fatalf("Fail to update transmission, %v", err) - } - transmissions, err := db.GetTransmissionsByStatus(0, contract.Failed) - if transmissions[0].Status != contract.Failed { - t.Fatalf("Unexpect test result. Transmission status '%s' not match %s", transmissions[0].Status, contract.Failed) - } - - // Test GetTransmissionsByNotificationSlug - transmissions, err = db.GetTransmissionsByNotificationSlug(slugName, 10) - if err != nil { - t.Fatalf("Fail to get transmission by notification slug, %v", err) - } - - if transmissions[0].Notification.Slug != slugName { - t.Fatalf("Unexpect test result. Slug '%v' not match '%v'", transmissions[0].Notification.Slug, slugName) - } - - // Test GetTransmissionsByStartEnd - resendCount := 2 - amount := 10 - beforeTime := dbp.MakeTimestamp() - err = populateTransmission(db, amount, resendCount) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - afterTime := dbp.MakeTimestamp() - transmissions, err = db.GetTransmissionsByStartEnd(beforeTime, afterTime, amount) - if err != nil { - t.Fatalf("Fail to get transmission by start time and end time, %v", err) - } - if len(transmissions) != amount { - t.Fatalf("Unexpect result. The amount of transmissions should be %v, but actually is %v", amount, len(transmissions)) - } - - // Test GetTransmissionsByStart - transmissions, err = db.GetTransmissionsByStart(beforeTime, amount) - if err != nil { - t.Fatalf("Fail to get transmission by start time, %v", err) - } - if len(transmissions) != amount { - t.Fatalf("Unexpect result. The amount of transmissions should be %v, but actually is %v", amount, len(transmissions)) - } - - // Test GetTransmissionsByEnd - transmissions, err = db.GetTransmissionsByEnd(afterTime, amount) - if err != nil { - t.Fatalf("Fail to get transmission by start time, %v", err) - } - if len(transmissions) != amount { - t.Fatalf("Unexpect result. The amount of transmissions should be %v, but actually is %v", amount, len(transmissions)) - } - - // Test DeleteTransmission - beforeTime = dbp.MakeTimestamp() - err = populateTransmission(db, 5, 1) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - afterTime = dbp.MakeTimestamp() - err = db.DeleteTransmission(afterTime-beforeTime, contract.Sent) - if err != nil { - t.Fatalf("Fail to delete old transmission, %v", err) - } -} - -func getNotification(slug string, status contract.NotificationsStatus) contract.Notification { - n := contract.Notification{} - n.Slug = slug - n.Sender = "test-sender" - n.Category = contract.Hwhealth - n.Severity = contract.Normal - n.Content = "The machine is running for 25 days." - n.Labels = []string{"labelA", "labelB"} - n.Status = status - n.Description = "Notify running time" - return n -} - -func populateNotification(db interfaces.DBClient, from int, to int, status contract.NotificationsStatus) error { - for i := from; i < to; i++ { - n := getNotification(fmt.Sprintf("slug-%d", i), status) - _, err := db.AddNotification(n) - if err != nil { - return err - } - } - return nil -} - -func getSubscription(slug string) contract.Subscription { - s := contract.Subscription{} - s.Slug = slug - s.Receiver = "test-receiver" - s.Description = "Subscription test" - s.SubscribedCategories = []contract.NotificationsCategory{contract.Security, contract.Hwhealth, contract.Swhealth} - s.SubscribedLabels = []string{"labelA", "labelB"} - return s -} - -func cleanUpAllSubscription(db interfaces.DBClient) error { - subscriptions, err := db.GetSubscriptions() - if err != nil { - return err - } - for _, s := range subscriptions { - err = db.DeleteSubscriptionBySlug(s.Slug) - if err != nil { - return err - } - } - return nil -} - -func populateSubscription(db interfaces.DBClient, count int) error { - for i := 0; i < count; i++ { - s := getSubscription(fmt.Sprintf("slug-%d", i)) - _, err := db.AddSubscription(s) - if err != nil { - return err - } - } - return nil -} - -func getTransmission(slug string, resendCount int) contract.Transmission { - t := contract.Transmission{} - t.Notification = getNotification(slug, contract.New) - t.Channel = contract.Channel{ - Type: contract.Email, - } - t.Receiver = "test-receiver" - t.Status = contract.Sent - t.ResendCount = resendCount - return t -} - -func populateTransmission(db interfaces.DBClient, count int, resendCount int) error { - for i := 0; i < count; i++ { - t := getTransmission(fmt.Sprintf("slug-%d", i), resendCount) - _, err := db.AddTransmission(t) - if err != nil { - return err - } - } - return nil -} diff --git a/internal/pkg/db/test/db_scheduler.go b/internal/pkg/db/test/db_scheduler.go deleted file mode 100644 index 9c35f93be9..0000000000 --- a/internal/pkg/db/test/db_scheduler.go +++ /dev/null @@ -1,288 +0,0 @@ -// -// Copyright (c) 2018 Cavium -// -// SPDX-License-Identifier: Apache-2.0 -// - -package test - -import ( - "fmt" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/interfaces" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -func TestSchedulerDB(t *testing.T, db interfaces.DBClient) { - testDBInterval(t, db) - testDBIntervalAction(t, db) - - db.CloseSession() - // Calling CloseSession twice to test that there is no panic when closing an - // already closed db - db.CloseSession() -} - -func populateIntervals(db interfaces.DBClient, count int) (string, error) { - var id string - for i := 0; i < count; i++ { - name := fmt.Sprintf("name%d", i) - mi := contract.Interval{} - mi.Name = name - var err error - id, err = db.AddInterval(mi) - if err != nil { - return id, err - } - } - return id, nil -} - -func populateIntervalActions(db interfaces.DBClient, count int) (string, error) { - var id string - for i := 0; i < count; i++ { - mi := contract.IntervalAction{} - mi.Name = fmt.Sprintf("name%d", i) - mi.Target = fmt.Sprintf("target%d", i) - mi.Interval = fmt.Sprintf("interval%d", i) - var err error - id, err = db.AddIntervalAction(mi) - if err != nil { - return id, err - } - } - return id, nil -} - -func testDBInterval(t *testing.T, db interfaces.DBClient) { - _, err := db.ScrubAllIntervals() - if err != nil { - t.Fatalf("Error removing all intervals") - } - - mis, err := db.Intervals() - if err != nil { - t.Fatalf("Error getting intervals %v", err) - } - if len(mis) != 0 { - t.Fatalf("There should be 0 mis instead of %d", len(mis)) - } - - id, err := populateIntervals(db, 110) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - - _, err = populateIntervals(db, 110) - if err == nil { - t.Fatalf("Should be an error adding a new interval with the same name\n") - } - - mis, err = db.Intervals() - if err != nil { - t.Fatalf("Error getting intervals %v", err) - } - if len(mis) != 110 { - t.Fatalf("There should be 110 intervals instead of %d", len(mis)) - } - - mis, err = db.IntervalsWithLimit(50) - if err != nil { - t.Fatalf("Error getting intervals with limit: %v", err) - } - if len(mis) != 50 { - t.Fatalf("There should be 50 intervals, not %d", len(mis)) - } - - mi, err := db.IntervalById(id) - if err != nil { - t.Fatalf("Error getting interval by id %v", err) - } - if mi.ID != id { - t.Fatalf("Id does not match %s - %s", mi.ID, id) - } - - _, err = db.IntervalById("INVALID") - if err == nil { - t.Fatalf("Interval should not be found") - } - - mi, err = db.IntervalByName("name1") - if err != nil { - t.Fatalf("Error getting interval by id %v", err) - } - if mi.Name != "name1" { - t.Fatalf("Name does not match %s - name1", mi.Name) - } - _, err = db.IntervalByName("INVALID") - if err == nil { - t.Fatalf("Interval should not be found") - } - - mi = contract.Interval{} - mi.ID = id - mi.Name = "name" - err = db.UpdateInterval(mi) - if err != nil { - t.Fatalf("Error updating interval %v", err) - } - mi2, err := db.IntervalById(mi.ID) - if err != nil { - t.Fatalf("Error getting interval by id %v", err) - } - if mi2.Name != mi.Name { - t.Fatalf("Did not update interval correctly: %s %s", mi.Name, mi2.Name) - } - - err = db.DeleteIntervalById("INVALID") - if err == nil { - t.Fatalf("Interval should not be deleted") - } - - err = db.DeleteIntervalById(id) - if err != nil { - t.Fatalf("Interval should be deleted: %v", err) - } - - err = db.UpdateInterval(mi) - if err == nil { - t.Fatalf("Update should return error") - } - - _, err = db.ScrubAllIntervals() - if err != nil { - t.Fatalf("Error removing all intervals") - } -} - -func testDBIntervalAction(t *testing.T, db interfaces.DBClient) { - _, err := db.ScrubAllIntervalActions() - if err != nil { - t.Fatalf("Error removing all IntervalActions") - } - - ias, err := db.IntervalActions() - if err != nil { - t.Fatalf("Error getting IntervalActions %v", err) - } - if len(ias) != 0 { - t.Fatalf("There should be 0 ias instead of %d", len(ias)) - } - - id, err := populateIntervalActions(db, 110) - if err != nil { - t.Fatalf("Error populating db: %v\n", err) - } - - _, err = populateIntervalActions(db, 110) - if err == nil { - t.Fatalf("Should be an error adding a new IntervalAction with the same name\n") - } - - ias, err = db.IntervalActions() - if err != nil { - t.Fatalf("Error getting IntervalActions %v", err) - } - if len(ias) != 110 { - t.Fatalf("There should be 110 IntervalActions instead of %d", len(ias)) - } - - ias, err = db.IntervalActionsWithLimit(50) - if err != nil { - t.Fatalf("Error getting IntervalActions with limit: %v", err) - } - if len(ias) != 50 { - t.Fatalf("There should be 50 IntervalActions, not %d", len(ias)) - } - - ia, err := db.IntervalActionById(id) - if err != nil { - t.Fatalf("Error getting IntervalAction by id %v", err) - } - if ia.ID != id { - t.Fatalf("Id does not match %s - %s", ia.ID, id) - } - - _, err = db.IntervalActionById("INVALID") - if err == nil { - t.Fatalf("IntervalAction should not be found") - } - - ia, err = db.IntervalActionByName("name1") - if err != nil { - t.Fatalf("Error getting IntervalAction by id %v", err) - } - if ia.Name != "name1" { - t.Fatalf("Name does not match %s - name1", ia.Name) - } - _, err = db.IntervalActionByName("INVALID") - if err == nil { - t.Fatalf("IntervalAction should not be found") - } - - ias, err = db.IntervalActionsByTarget("target1") - if err != nil { - t.Fatalf("Error getting IntervalActionsByTarget: %v", err) - } - if len(ias) != 1 { - t.Fatalf("There should be 1 IntervalActions, not %d", len(ias)) - } - ias, err = db.IntervalActionsByTarget("INVALID") - if err != nil { - t.Fatalf("Error getting IntervalActionsByTarget: %v", err) - } - if len(ias) != 0 { - t.Fatalf("There should be 0 IntervalActions, not %d", len(ias)) - } - - ias, err = db.IntervalActionsByIntervalName("interval1") - if err != nil { - t.Fatalf("Error getting IntervalActionsByIntervalName: %v", err) - } - if len(ias) != 1 { - t.Fatalf("There should be 1 IntervalActions, not %d", len(ias)) - } - ias, err = db.IntervalActionsByIntervalName("INVALID") - if err != nil { - t.Fatalf("Error getting IntervalActionsByIntervalName: %v", err) - } - if len(ias) != 0 { - t.Fatalf("There should be 0 IntervalActions, not %d", len(ias)) - } - - ia = contract.IntervalAction{} - ia.ID = id - ia.Name = "name" - err = db.UpdateIntervalAction(ia) - if err != nil { - t.Fatalf("Error updating IntervalAction %v", err) - } - ia2, err := db.IntervalActionById(ia.ID) - if err != nil { - t.Fatalf("Error getting IntervalAction by id %v", err) - } - if ia2.Name != ia.Name { - t.Fatalf("Did not update IntervalAction correctly: %s %s", ia.Name, ia2.Name) - } - - err = db.DeleteIntervalActionById("INVALID") - if err == nil { - t.Fatalf("IntervalAction should not be deleted") - } - - err = db.DeleteIntervalActionById(id) - if err != nil { - t.Fatalf("IntervalAction should be deleted: %v", err) - } - - err = db.UpdateIntervalAction(ia) - if err == nil { - t.Fatalf("Update should return error") - } - - _, err = db.ScrubAllIntervalActions() - if err != nil { - t.Fatalf("Error removing all IntervalActions") - } -} diff --git a/internal/pkg/errorconcept/addressable.go b/internal/pkg/errorconcept/addressable.go deleted file mode 100644 index 4642071945..0000000000 --- a/internal/pkg/errorconcept/addressable.go +++ /dev/null @@ -1,90 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 errorconcept - -import ( - "net/http" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" -) - -var Addressable addressableErrorConcept - -// AddressableErrorConcept encapsulates error concepts which pertain to addressables -type addressableErrorConcept struct { - EmptyName addressableEmptyName - InUse addressableInUse - InvalidRequest_StatusInternalServer addressableInvalidRequest_StatusInternalServer - NotFound addressableNotFound -} - -type addressableEmptyName struct{} - -func (r addressableEmptyName) httpErrorCode() int { - return http.StatusBadRequest -} - -func (r addressableEmptyName) isA(err error) bool { - _, ok := err.(errors.ErrEmptyAddressableName) - return ok -} - -func (r addressableEmptyName) message(err error) string { - return err.Error() -} - -type addressableInUse struct{} - -func (r addressableInUse) httpErrorCode() int { - return http.StatusConflict -} - -func (r addressableInUse) isA(err error) bool { - _, ok := err.(errors.ErrAddressableInUse) - return ok -} - -func (r addressableInUse) message(err error) string { - return err.Error() -} - -type addressableNotFound struct{} - -func (r addressableNotFound) httpErrorCode() int { - return http.StatusNotFound -} - -func (r addressableNotFound) isA(err error) bool { - _, ok := err.(errors.ErrAddressableNotFound) - return ok -} - -func (r addressableNotFound) message(err error) string { - return err.Error() -} - -type addressableInvalidRequest_StatusInternalServer struct{} - -func (r addressableInvalidRequest_StatusInternalServer) httpErrorCode() int { - return http.StatusInternalServerError -} - -func (r addressableInvalidRequest_StatusInternalServer) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r addressableInvalidRequest_StatusInternalServer) message(err error) string { - return err.Error() -} diff --git a/internal/pkg/errorconcept/cbor.go b/internal/pkg/errorconcept/cbor.go deleted file mode 100644 index 4dfe282d1a..0000000000 --- a/internal/pkg/errorconcept/cbor.go +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 errorconcept - -import ( - "net/http" - - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" -) - -var CBOR cborErrorConcept - -// eventErrorConcept represents the accessor for the event-specific error concepts -type cborErrorConcept struct { - NotSupported cborNotSupported -} - -type cborNotSupported struct{} - -func (r cborNotSupported) httpErrorCode() int { - return http.StatusNotImplemented -} - -func (r cborNotSupported) isA(err error) bool { - _, ok := err.(errors.ErrCBORNotSupported) - return ok -} - -func (r cborNotSupported) message(err error) string { - return err.Error() -} diff --git a/internal/pkg/errorconcept/command.go b/internal/pkg/errorconcept/command.go deleted file mode 100644 index 45bb793654..0000000000 --- a/internal/pkg/errorconcept/command.go +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 errorconcept - -import ( - "net/http" - - "github.com/edgexfoundry/edgex-go/internal/core/command/errors" -) - -var Command commandErrorConcept - -// ValueDescriptorsErrorConcept represents the accessor for the value-descriptor-specific error concepts -type commandErrorConcept struct { - NotAssociatedWithDevice commandNotAssociatedWithDevice -} - -type commandNotAssociatedWithDevice struct{} - -func (r commandNotAssociatedWithDevice) httpErrorCode() int { - return http.StatusNotFound -} - -func (r commandNotAssociatedWithDevice) isA(err error) bool { - _, ok := err.(errors.ErrCommandNotAssociatedWithDevice) - return ok -} - -func (r commandNotAssociatedWithDevice) message(err error) string { - return err.Error() -} diff --git a/internal/pkg/errorconcept/common.go b/internal/pkg/errorconcept/common.go deleted file mode 100644 index 97007fe4be..0000000000 --- a/internal/pkg/errorconcept/common.go +++ /dev/null @@ -1,231 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 errorconcept - -import ( - "net/http" - - data "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - metadata "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -var Common commonErrorConcept - -// CommonErrorConcept represents error concepts which apply across core-services -type commonErrorConcept struct { - ContractInvalid_StatusBadRequest contractInvalid_StatusBadRequest - DeleteError deleteError - DuplicateName duplicateName - InvalidID invalidID - InvalidRequest_StatusBadRequest invalidRequest_BadRequest - InvalidRequest_StatusServiceUnavailable invalidRequest_StatusServiceUnavailable - ItemNotFound itemNotFound - JsonDecoding jsonDecoding - LimitExceeded errLimitExceeded - RetrieveError_StatusInternalServer retrieveError_StatusInternalServer - RetrieveError_StatusServiceUnavailable retrieveError_ServiceUnavailable - UpdateError_StatusInternalServer updateError_StatusInternalServer - UpdateError_StatusServiceUnavailable updateError_StatusServiceUnavailable -} - -type contractInvalid_StatusBadRequest struct{} - -func (r contractInvalid_StatusBadRequest) httpErrorCode() int { - return http.StatusBadRequest -} - -func (r contractInvalid_StatusBadRequest) isA(err error) bool { - _, ok := err.(models.ErrContractInvalid) - return ok -} - -func (r contractInvalid_StatusBadRequest) message(err error) string { - return err.Error() -} - -type deleteError struct{} - -func (r deleteError) httpErrorCode() int { - return http.StatusServiceUnavailable -} - -func (r deleteError) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r deleteError) message(err error) string { - return err.Error() -} - -type duplicateName struct{} - -func (r duplicateName) httpErrorCode() int { - return http.StatusConflict -} - -func (r duplicateName) isA(err error) bool { - _, ok := err.(metadata.ErrDuplicateName) - return ok -} - -func (r duplicateName) message(err error) string { - return err.Error() -} - -type invalidID struct{} - -func (r invalidID) httpErrorCode() int { - return http.StatusBadRequest -} - -func (r invalidID) isA(err error) bool { - _, ok := err.(data.ErrInvalidId) - return ok -} - -func (r invalidID) message(err error) string { - return err.Error() -} - -type invalidRequest_BadRequest struct{} - -func (r invalidRequest_BadRequest) httpErrorCode() int { - return http.StatusBadRequest -} - -func (r invalidRequest_BadRequest) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r invalidRequest_BadRequest) message(err error) string { - return err.Error() -} - -type invalidRequest_StatusServiceUnavailable struct{} - -func (r invalidRequest_StatusServiceUnavailable) httpErrorCode() int { - return http.StatusServiceUnavailable -} - -func (r invalidRequest_StatusServiceUnavailable) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r invalidRequest_StatusServiceUnavailable) message(err error) string { - return err.Error() -} - -type itemNotFound struct{} - -func (r itemNotFound) httpErrorCode() int { - return http.StatusNotFound -} - -func (r itemNotFound) isA(err error) bool { - _, ok := err.(metadata.ErrItemNotFound) - return ok -} - -func (r itemNotFound) message(err error) string { - return err.Error() -} - -type jsonDecoding struct{} - -func (r jsonDecoding) httpErrorCode() int { - return http.StatusBadRequest -} - -func (r jsonDecoding) isA(err error) bool { - _, ok := err.(data.ErrJsonDecoding) - return ok -} - -func (r jsonDecoding) message(err error) string { - return err.Error() -} - -type errLimitExceeded struct{} - -func (r errLimitExceeded) httpErrorCode() int { - return http.StatusRequestEntityTooLarge -} - -func (r errLimitExceeded) isA(err error) bool { - _, ok := err.(metadata.ErrLimitExceeded) - return ok -} - -func (r errLimitExceeded) message(err error) string { - return err.Error() -} - -type retrieveError_StatusInternalServer struct{} - -func (r retrieveError_StatusInternalServer) httpErrorCode() int { - return http.StatusInternalServerError -} - -func (r retrieveError_StatusInternalServer) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r retrieveError_StatusInternalServer) message(err error) string { - return err.Error() -} - -type retrieveError_ServiceUnavailable struct{} - -func (r retrieveError_ServiceUnavailable) httpErrorCode() int { - return http.StatusServiceUnavailable -} - -func (r retrieveError_ServiceUnavailable) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r retrieveError_ServiceUnavailable) message(err error) string { - return err.Error() -} - -type updateError_StatusInternalServer struct{} - -func (r updateError_StatusInternalServer) httpErrorCode() int { - return http.StatusInternalServerError -} - -func (r updateError_StatusInternalServer) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r updateError_StatusInternalServer) message(err error) string { - return err.Error() -} - -type updateError_StatusServiceUnavailable struct{} - -func (r updateError_StatusServiceUnavailable) httpErrorCode() int { - return http.StatusServiceUnavailable -} - -func (r updateError_StatusServiceUnavailable) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r updateError_StatusServiceUnavailable) message(err error) string { - return err.Error() -} diff --git a/internal/pkg/errorconcept/const.go b/internal/pkg/errorconcept/const.go deleted file mode 100644 index 1b466dc135..0000000000 --- a/internal/pkg/errorconcept/const.go +++ /dev/null @@ -1,19 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 errorconcept - -const ( - METHOD_NOT_ALLOWED = "isA should not be invoked, this is to only be used as a default error concept" -) diff --git a/internal/pkg/errorconcept/db.go b/internal/pkg/errorconcept/db.go deleted file mode 100644 index 441de7442e..0000000000 --- a/internal/pkg/errorconcept/db.go +++ /dev/null @@ -1,89 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 errorconcept - -import ( - "net/http" - - data "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" -) - -var Database databaseErrorConcept - -// DatabaseErrorConcept represents a collection of database error concepts -type databaseErrorConcept struct { - NotFound dbNotFound - NotFoundTyped dbNotFoundTyped - NotUnique dbNotUnique - InvalidObjectId dbInvalidObjectId -} - -type dbNotFound struct{} - -func (r dbNotFound) httpErrorCode() int { - return http.StatusNotFound -} - -func (r dbNotFound) isA(err error) bool { - return err == db.ErrNotFound -} - -func (r dbNotFound) message(err error) string { - return err.Error() -} - -type dbNotFoundTyped struct{} - -func (r dbNotFoundTyped) httpErrorCode() int { - return http.StatusNotFound -} - -func (r dbNotFoundTyped) isA(err error) bool { - _, ok := err.(data.ErrDbNotFound) - return ok -} - -func (r dbNotFoundTyped) message(err error) string { - return err.Error() -} - -type dbNotUnique struct{} - -func (r dbNotUnique) httpErrorCode() int { - return http.StatusConflict -} - -func (r dbNotUnique) isA(err error) bool { - return err == db.ErrNotUnique -} - -func (r dbNotUnique) message(err error) string { - return err.Error() -} - -type dbInvalidObjectId struct{} - -func (r dbInvalidObjectId) httpErrorCode() int { - return http.StatusBadRequest -} - -func (r dbInvalidObjectId) isA(err error) bool { - return err == db.ErrInvalidObjectId -} - -func (r dbInvalidObjectId) message(err error) string { - return err.Error() -} diff --git a/internal/pkg/errorconcept/default.go b/internal/pkg/errorconcept/default.go deleted file mode 100644 index a72d96cc69..0000000000 --- a/internal/pkg/errorconcept/default.go +++ /dev/null @@ -1,115 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 errorconcept - -import ( - "net/http" -) - -var Default defaultErrorConcept - -// DefaultErrorConcept represents a fallback error concept only -type defaultErrorConcept struct { - BadRequest badRequest - RequestEntityTooLarge requestEntityTooLarge - InternalServerError internalServerError - ServiceUnavailable serviceUnavailable - NotFound notFound - Conflict conflict -} - -type badRequest struct{} - -func (r badRequest) httpErrorCode() int { - return http.StatusBadRequest -} - -func (r badRequest) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r badRequest) message(err error) string { - return err.Error() -} - -type requestEntityTooLarge struct{} - -func (r requestEntityTooLarge) httpErrorCode() int { - return http.StatusRequestEntityTooLarge -} - -func (r requestEntityTooLarge) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r requestEntityTooLarge) message(err error) string { - return err.Error() -} - -type internalServerError struct{} - -func (r internalServerError) httpErrorCode() int { - return http.StatusInternalServerError -} - -func (r internalServerError) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r internalServerError) message(err error) string { - return err.Error() -} - -type serviceUnavailable struct{} - -func (r serviceUnavailable) httpErrorCode() int { - return http.StatusServiceUnavailable -} - -func (r serviceUnavailable) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r serviceUnavailable) message(err error) string { - return err.Error() -} - -type notFound struct{} - -func (r notFound) httpErrorCode() int { - return http.StatusNotFound -} - -func (r notFound) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r notFound) message(err error) string { - return err.Error() -} - -type conflict struct{} - -func (r conflict) httpErrorCode() int { - return http.StatusConflict -} - -func (r conflict) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r conflict) message(err error) string { - return err.Error() -} diff --git a/internal/pkg/errorconcept/device.go b/internal/pkg/errorconcept/device.go deleted file mode 100644 index 87a534c7ad..0000000000 --- a/internal/pkg/errorconcept/device.go +++ /dev/null @@ -1,104 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 errorconcept - -import ( - "net/http" - - command "github.com/edgexfoundry/edgex-go/internal/core/command/errors" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" -) - -var Device deviceErrorConcept - -// DeviceErrorConcept represents the accessor for the device-specific error concepts -type deviceErrorConcept struct { - Locked deviceLocked - NotFound deviceNotFound - NotFoundInDB deviceNotFoundInDB - NotifyError deviceNotify - RequesterError deviceRequester -} - -type deviceLocked struct{} - -func (r deviceLocked) httpErrorCode() int { - return http.StatusLocked -} - -func (r deviceLocked) isA(err error) bool { - _, ok := err.(command.ErrDeviceLocked) - return ok -} - -func (r deviceLocked) message(err error) string { - return err.Error() -} - -type deviceNotFound struct{} - -func (r deviceNotFound) httpErrorCode() int { - return http.StatusNotFound -} - -func (r deviceNotFound) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r deviceNotFound) message(err error) string { - return err.Error() -} - -type deviceNotFoundInDB struct{} - -func (r deviceNotFoundInDB) httpErrorCode() int { - return http.StatusNotFound -} - -func (r deviceNotFoundInDB) isA(err error) bool { - return err == db.ErrNotFound -} - -func (r deviceNotFoundInDB) message(err error) string { - return "Device not found" -} - -type deviceNotify struct{} - -func (r deviceNotify) httpErrorCode() int { - return http.StatusServiceUnavailable -} - -func (r deviceNotify) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r deviceNotify) message(err error) string { - return err.Error() -} - -type deviceRequester struct{} - -func (r deviceRequester) httpErrorCode() int { - return http.StatusInternalServerError -} - -func (r deviceRequester) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r deviceRequester) message(err error) string { - return err.Error() -} diff --git a/internal/pkg/errorconcept/device_profile.go b/internal/pkg/errorconcept/device_profile.go deleted file mode 100644 index d8ff8e5294..0000000000 --- a/internal/pkg/errorconcept/device_profile.go +++ /dev/null @@ -1,216 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 errorconcept - -import ( - "net/http" - - metadataErrors "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -var DeviceProfile deviceProfileErrorConcept - -// DeviceProfileErrorConcept represents the accessor for the device-profile-specific error concepts -type deviceProfileErrorConcept struct { - ContractInvalid_StatusConflict deviceProfileContractInvalid_StatusConflict - DuplicateName deviceProfileDuplicateName - EmptyName deviceProfileEmptyName - InvalidState_StatusBadRequest deviceProfileInvalidState_StatusBadRequest - InvalidState_StatusConflict deviceProfileInvalidState_StatusConflict - MarshalYaml deviceProfileMarshalYaml - MarshalJson deviceProfileMarshalJson - MissingFile deviceProfileMissingFile - NotFound deviceProfileNotFound - ReadFile deviceProfileReadFile - UnmarshalYaml_StatusInternalServer deviceProfileUnmarshalYaml_StatusInternalServer - UnmarshalYaml_StatusServiceUnavailable deviceProfileUnmarshalYaml_StatusServiceUnavailable -} - -type deviceProfileContractInvalid_StatusConflict struct{} - -func (r deviceProfileContractInvalid_StatusConflict) httpErrorCode() int { - return http.StatusConflict -} - -func (r deviceProfileContractInvalid_StatusConflict) isA(err error) bool { - _, ok := err.(models.ErrContractInvalid) - return ok -} - -func (r deviceProfileContractInvalid_StatusConflict) message(err error) string { - return err.Error() -} - -type deviceProfileDuplicateName struct { - err error -} - -func (r deviceProfileDuplicateName) httpErrorCode() int { - return http.StatusConflict -} - -func (r deviceProfileDuplicateName) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r deviceProfileDuplicateName) message(err error) string { - return "Duplicate name for device profile" -} - -type deviceProfileEmptyName struct{} - -func (r deviceProfileEmptyName) httpErrorCode() int { - return http.StatusBadRequest -} - -func (r deviceProfileEmptyName) isA(err error) bool { - _, ok := err.(metadataErrors.ErrEmptyDeviceProfileName) - return ok -} - -func (r deviceProfileEmptyName) message(err error) string { - return err.Error() -} - -type deviceProfileInvalidState_StatusBadRequest struct{} - -func (r deviceProfileInvalidState_StatusBadRequest) httpErrorCode() int { - return http.StatusBadRequest -} - -func (r deviceProfileInvalidState_StatusBadRequest) isA(err error) bool { - _, ok := err.(metadataErrors.ErrDeviceProfileInvalidState) - return ok -} - -func (r deviceProfileInvalidState_StatusBadRequest) message(err error) string { - return err.Error() -} - -type deviceProfileInvalidState_StatusConflict struct{} - -func (r deviceProfileInvalidState_StatusConflict) httpErrorCode() int { - return http.StatusConflict -} - -func (r deviceProfileInvalidState_StatusConflict) isA(err error) bool { - _, ok := err.(metadataErrors.ErrDeviceProfileInvalidState) - return ok -} - -func (r deviceProfileInvalidState_StatusConflict) message(err error) string { - return err.Error() -} - -type deviceProfileMarshalJson struct{} - -func (r deviceProfileMarshalJson) httpErrorCode() int { - return http.StatusInternalServerError -} - -func (r deviceProfileMarshalJson) isA(err error) bool { - _, ok := err.(metadataErrors.ErrDeviceProfileMarshalJson) - return ok -} - -func (r deviceProfileMarshalJson) message(err error) string { - return err.Error() -} - -type deviceProfileMarshalYaml struct{} - -func (r deviceProfileMarshalYaml) httpErrorCode() int { - return http.StatusInternalServerError -} - -func (r deviceProfileMarshalYaml) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r deviceProfileMarshalYaml) message(err error) string { - return err.Error() -} - -type deviceProfileMissingFile struct{} - -func (r deviceProfileMissingFile) httpErrorCode() int { - return http.StatusBadRequest -} - -func (r deviceProfileMissingFile) isA(err error) bool { - return err == http.ErrMissingFile -} - -func (r deviceProfileMissingFile) message(err error) string { - return metadataErrors.NewErrEmptyFile("YAML").Error() -} - -type deviceProfileNotFound struct{} - -func (r deviceProfileNotFound) httpErrorCode() int { - return http.StatusNotFound -} - -func (r deviceProfileNotFound) isA(err error) bool { - _, ok := err.(metadataErrors.ErrDeviceProfileNotFound) - return ok -} - -func (r deviceProfileNotFound) message(err error) string { - return err.Error() -} - -type deviceProfileReadFile struct{} - -func (r deviceProfileReadFile) httpErrorCode() int { - return http.StatusInternalServerError -} - -func (r deviceProfileReadFile) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r deviceProfileReadFile) message(err error) string { - return err.Error() -} - -type deviceProfileUnmarshalYaml_StatusInternalServer struct{} - -func (r deviceProfileUnmarshalYaml_StatusInternalServer) httpErrorCode() int { - return http.StatusInternalServerError -} - -func (r deviceProfileUnmarshalYaml_StatusInternalServer) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r deviceProfileUnmarshalYaml_StatusInternalServer) message(err error) string { - return err.Error() -} - -type deviceProfileUnmarshalYaml_StatusServiceUnavailable struct{} - -func (r deviceProfileUnmarshalYaml_StatusServiceUnavailable) httpErrorCode() int { - return http.StatusServiceUnavailable -} - -func (r deviceProfileUnmarshalYaml_StatusServiceUnavailable) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r deviceProfileUnmarshalYaml_StatusServiceUnavailable) message(err error) string { - return err.Error() -} diff --git a/internal/pkg/errorconcept/device_report.go b/internal/pkg/errorconcept/device_report.go deleted file mode 100644 index adefb95407..0000000000 --- a/internal/pkg/errorconcept/device_report.go +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 errorconcept - -import ( - "net/http" - - "github.com/edgexfoundry/edgex-go/internal/pkg/db" -) - -var DeviceReport deviceReportErrorConcept - -// DeviceReportErrorConcept represents the accessor for the device-report-specific error concepts -type deviceReportErrorConcept struct { - DeviceNotFound deviceReportDeviceNotFound - NotUnique deviceReportNotUnique -} - -type deviceReportDeviceNotFound struct{} - -func (r deviceReportDeviceNotFound) httpErrorCode() int { - return http.StatusNotFound -} - -func (r deviceReportDeviceNotFound) isA(err error) bool { - return err == db.ErrNotFound -} - -func (r deviceReportDeviceNotFound) message(err error) string { - return "Device referenced by Device Report doesn't exist" -} - -type deviceReportNotUnique struct{} - -func (r deviceReportNotUnique) httpErrorCode() int { - return http.StatusConflict -} - -func (r deviceReportNotUnique) isA(err error) bool { - return err == db.ErrNotUnique -} - -func (r deviceReportNotUnique) message(err error) string { - return "Duplicate Name for the device report" -} diff --git a/internal/pkg/errorconcept/device_service.go b/internal/pkg/errorconcept/device_service.go deleted file mode 100644 index 9eca401200..0000000000 --- a/internal/pkg/errorconcept/device_service.go +++ /dev/null @@ -1,140 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 errorconcept - -import ( - "net/http" - - "github.com/edgexfoundry/edgex-go/internal/pkg/db" -) - -var DeviceService deviceServiceErrorConcept - -// DeviceServiceErrorConcept represents the accessor for the device-service-specific error concepts -type deviceServiceErrorConcept struct { - AddressableNotFound deviceServiceAddressableNotFound - EmptyAddressable deviceServiceEmptyAddressable - InvalidRequest_StatusInternalServer deviceServiceInvalidRequest_StatusInternalServer - InvalidState deviceServiceInvalidState - NotUnique deviceServiceNotUnique - NotFound deviceServiceNotFound -} - -type deviceServiceAddressableNotFound struct{} - -func (r deviceServiceAddressableNotFound) httpErrorCode() int { - return http.StatusNotFound -} - -func (r deviceServiceAddressableNotFound) isA(err error) bool { - return err == db.ErrNotFound -} - -func (r deviceServiceAddressableNotFound) message(err error) string { - return "Addressable not found by ID or Name" -} - -type deviceServiceEmptyAddressable struct{} - -func (r deviceServiceEmptyAddressable) httpErrorCode() int { - return http.StatusBadRequest -} - -func (r deviceServiceEmptyAddressable) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r deviceServiceEmptyAddressable) message(err error) string { - return "Must provide an Addressable for Device Service" -} - -type deviceServiceInvalidRequest_StatusInternalServer struct{} - -func (r deviceServiceInvalidRequest_StatusInternalServer) httpErrorCode() int { - return http.StatusInternalServerError -} - -func (r deviceServiceInvalidRequest_StatusInternalServer) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r deviceServiceInvalidRequest_StatusInternalServer) message(err error) string { - return err.Error() -} - -type deviceServiceInvalidState struct{} - -func (r deviceServiceInvalidState) httpErrorCode() int { - return http.StatusBadRequest -} - -func (r deviceServiceInvalidState) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r deviceServiceInvalidState) message(err error) string { - return err.Error() -} - -// NewDeviceServiceDuplicate instantiates a new deviceServiceDuplicate error concept in effort to handle stateful -// DeviceService duplicate errors -func NewDeviceServiceDuplicate(currentDSId string, newDSId string) deviceServiceDuplicate { - return deviceServiceDuplicate{CurrentDSId: currentDSId, NewDSId: newDSId} -} - -type deviceServiceDuplicate struct { - CurrentDSId string - NewDSId string -} - -func (r deviceServiceDuplicate) httpErrorCode() int { - return http.StatusConflict -} - -func (r deviceServiceDuplicate) isA(err error) bool { - return r.CurrentDSId != r.NewDSId -} - -func (r deviceServiceDuplicate) message(err error) string { - return "Duplicate name for Device Service" -} - -type deviceServiceNotUnique struct{} - -func (r deviceServiceNotUnique) httpErrorCode() int { - return http.StatusConflict -} - -func (r deviceServiceNotUnique) isA(err error) bool { - return err == db.ErrNotUnique -} - -func (r deviceServiceNotUnique) message(err error) string { - return "Duplicate name for the device service" -} - -type deviceServiceNotFound struct{} - -func (r deviceServiceNotFound) httpErrorCode() int { - return http.StatusNotFound -} - -func (r deviceServiceNotFound) isA(err error) bool { - return err == db.ErrNotFound -} - -func (r deviceServiceNotFound) message(err error) string { - return "Device service not found" -} diff --git a/internal/pkg/errorconcept/event.go b/internal/pkg/errorconcept/event.go deleted file mode 100644 index 6728951fa4..0000000000 --- a/internal/pkg/errorconcept/event.go +++ /dev/null @@ -1,42 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 errorconcept - -import ( - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - "net/http" -) - -var Events eventErrorConcept - -// eventErrorConcept represents the accessor for the event-specific error concepts -type eventErrorConcept struct { - NotFound eventNotFound -} - -type eventNotFound struct{} - -func (r eventNotFound) httpErrorCode() int { - return http.StatusNotFound -} - -func (r eventNotFound) isA(err error) bool { - _, ok := err.(errors.ErrEventNotFound) - return ok -} - -func (r eventNotFound) message(err error) string { - return err.Error() -} diff --git a/internal/pkg/errorconcept/handler.go b/internal/pkg/errorconcept/handler.go deleted file mode 100644 index c631986ebe..0000000000 --- a/internal/pkg/errorconcept/handler.go +++ /dev/null @@ -1,71 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 errorconcept - -import ( - "net/http" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" -) - -type ErrorConceptType interface { - httpErrorCode() int - isA(err error) bool - message(err error) string -} - -type Handler struct { - logger logger.LoggingClient -} - -type ErrorHandler interface { - Handle(w http.ResponseWriter, err error, ec ErrorConceptType) - HandleManyVariants(w http.ResponseWriter, err error, allowableErrors []ErrorConceptType, defaultError ErrorConceptType) - HandleOneVariant(w http.ResponseWriter, err error, allowableError ErrorConceptType, defaultError ErrorConceptType) -} - -func NewErrorHandler(l logger.LoggingClient) ErrorHandler { - h := Handler{l} - return &h -} - -// Handle applies the specified error and error concept tot he HTTP response writer -func (e *Handler) Handle(w http.ResponseWriter, err error, ec ErrorConceptType) { - message := ec.message(err) - e.logger.Error(message) - http.Error(w, message, ec.httpErrorCode()) -} - -// HandleOneVariant applies general error-handling with a single allowable error and a default error to be used as a -// fallback when none of the allowable errors are matched -func (e *Handler) HandleOneVariant(w http.ResponseWriter, err error, allowableError ErrorConceptType, defaultError ErrorConceptType) { - if allowableError != nil && allowableError.isA(err) { - e.Handle(w, err, allowableError) - return - } - e.Handle(w, err, defaultError) -} - -// HandleManyVariants applies general error-handling for the specified set of allowable errors and a default error to be used -// as a fallback when none of the allowable errors are matched -func (e *Handler) HandleManyVariants(w http.ResponseWriter, err error, allowableErrors []ErrorConceptType, defaultError ErrorConceptType) { - for key := range allowableErrors { - if allowableErrors[key].isA(err) { - e.Handle(w, err, allowableErrors[key]) - return - } - } - e.Handle(w, err, defaultError) -} diff --git a/internal/pkg/errorconcept/provision_watcher.go b/internal/pkg/errorconcept/provision_watcher.go deleted file mode 100644 index 5e50d6d56b..0000000000 --- a/internal/pkg/errorconcept/provision_watcher.go +++ /dev/null @@ -1,194 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 errorconcept - -import ( - "net/http" - - "github.com/edgexfoundry/edgex-go/internal/core/metadata/errors" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" -) - -var ProvisionWatcher provisionWatcherErrorConcept - -// ProvisionWatcherErrorConcept represents the accessor for provision-watcher-specific error concepts -type provisionWatcherErrorConcept struct { - DeleteError_StatusInternalServer provisionWatcherDeleteError_StatusInternalServer - DeviceProfileNotFound_StatusConflict provisionWatcherDeviceProfileNotFound_StatusConflict - DeviceProfileNotFound_StatusNotFound provisionWatcherDeviceProfileNotFound_StatusNotFound - DeviceServiceNotFound_StatusConflict provisionWatcherDeviceServiceNotFound_StatusConflict - DeviceServiceNotFound_StatusNotFound provisionWatcherDeviceServiceNotFound_StatusNotFound - InvalidID provisionWatcherInvalidId - NameCollision provisionWatcherNameCollision - NotFoundById provisionWatcherNotFoundById - NotFoundByName provisionWatcherNotFoundByName - NotUnique provisionWatcherNotUnique - RetrieveError_StatusNotFound provisionWatcherRetrieveError_StatusNotFound -} - -type provisionWatcherDeleteError_StatusInternalServer struct{} - -func (r provisionWatcherDeleteError_StatusInternalServer) httpErrorCode() int { - return http.StatusInternalServerError -} - -func (r provisionWatcherDeleteError_StatusInternalServer) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r provisionWatcherDeleteError_StatusInternalServer) message(err error) string { - return "Error deleting provision watcher" -} - -type provisionWatcherDeviceProfileNotFound_StatusConflict struct{} - -func (r provisionWatcherDeviceProfileNotFound_StatusConflict) httpErrorCode() int { - return http.StatusConflict -} - -func (r provisionWatcherDeviceProfileNotFound_StatusConflict) isA(err error) bool { - return err == db.ErrNotFound -} - -func (r provisionWatcherDeviceProfileNotFound_StatusConflict) message(err error) string { - return "Device profile not found for provision watcher" -} - -type provisionWatcherDeviceProfileNotFound_StatusNotFound struct{} - -func (r provisionWatcherDeviceProfileNotFound_StatusNotFound) httpErrorCode() int { - return http.StatusNotFound -} - -func (r provisionWatcherDeviceProfileNotFound_StatusNotFound) isA(err error) bool { - return err == db.ErrNotFound -} - -func (r provisionWatcherDeviceProfileNotFound_StatusNotFound) message(err error) string { - return "Device profile not found" -} - -type provisionWatcherDeviceServiceNotFound_StatusConflict struct{} - -func (r provisionWatcherDeviceServiceNotFound_StatusConflict) httpErrorCode() int { - return http.StatusConflict -} - -func (r provisionWatcherDeviceServiceNotFound_StatusConflict) isA(err error) bool { - return err == db.ErrNotFound -} - -func (r provisionWatcherDeviceServiceNotFound_StatusConflict) message(err error) string { - return "Device service not found for provision watcher" -} - -type provisionWatcherDeviceServiceNotFound_StatusNotFound struct{} - -func (r provisionWatcherDeviceServiceNotFound_StatusNotFound) httpErrorCode() int { - return http.StatusNotFound -} - -func (r provisionWatcherDeviceServiceNotFound_StatusNotFound) isA(err error) bool { - return err == db.ErrNotFound -} - -func (r provisionWatcherDeviceServiceNotFound_StatusNotFound) message(err error) string { - return "Device service not found" -} - -type provisionWatcherNotFoundById struct{} - -func (r provisionWatcherNotFoundById) httpErrorCode() int { - return http.StatusNotFound -} - -func (r provisionWatcherNotFoundById) isA(err error) bool { - return err == db.ErrNotFound -} - -func (r provisionWatcherNotFoundById) message(err error) string { - return "Provision Watcher not found by ID: " + err.Error() -} - -type provisionWatcherNotFoundByName struct{} - -func (r provisionWatcherNotFoundByName) httpErrorCode() int { - return http.StatusNotFound -} - -func (r provisionWatcherNotFoundByName) isA(err error) bool { - return err == db.ErrNotFound -} - -func (r provisionWatcherNotFoundByName) message(err error) string { - return "Provision Watcher not found: " + err.Error() -} - -type provisionWatcherInvalidId struct{} - -func (r provisionWatcherInvalidId) httpErrorCode() int { - return http.StatusNotFound -} - -func (r provisionWatcherInvalidId) isA(err error) bool { - return err == db.ErrInvalidObjectId -} - -func (r provisionWatcherInvalidId) message(err error) string { - return db.ErrInvalidObjectId.Error() -} - -type provisionWatcherNameCollision struct{} - -func (r provisionWatcherNameCollision) httpErrorCode() int { - return http.StatusConflict -} - -func (r provisionWatcherNameCollision) isA(err error) bool { - _, isA := err.(errors.ErrNameCollision) - return isA -} - -func (r provisionWatcherNameCollision) message(err error) string { - return err.Error() -} - -type provisionWatcherNotUnique struct{} - -func (r provisionWatcherNotUnique) httpErrorCode() int { - return http.StatusConflict -} - -func (r provisionWatcherNotUnique) isA(err error) bool { - return err == db.ErrNotFound -} - -func (r provisionWatcherNotUnique) message(err error) string { - return "Duplicate name for the provision watcher" -} - -type provisionWatcherRetrieveError_StatusNotFound struct{} - -func (r provisionWatcherRetrieveError_StatusNotFound) httpErrorCode() int { - return http.StatusNotFound -} - -func (r provisionWatcherRetrieveError_StatusNotFound) isA(err error) bool { - panic(METHOD_NOT_ALLOWED) -} - -func (r provisionWatcherRetrieveError_StatusNotFound) message(err error) string { - return err.Error() -} diff --git a/internal/pkg/errorconcept/service_client.go b/internal/pkg/errorconcept/service_client.go deleted file mode 100644 index 05662da9ac..0000000000 --- a/internal/pkg/errorconcept/service_client.go +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 errorconcept - -import "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/types" - -// NewServiceClientHttpError represents the accessor for the service-client-specific error concepts -func NewServiceClientHttpError(err error) *serviceClientHttpError { - return &serviceClientHttpError{Err: err} -} - -type serviceClientHttpError struct { - Err error -} - -func (r serviceClientHttpError) httpErrorCode() int { - return r.Err.(types.ErrServiceClient).StatusCode -} - -func (r serviceClientHttpError) isA(err error) bool { - _, ok := err.(types.ErrServiceClient) - return ok -} - -func (r serviceClientHttpError) message(err error) string { - return err.Error() -} diff --git a/internal/pkg/errorconcept/value_descriptors.go b/internal/pkg/errorconcept/value_descriptors.go deleted file mode 100644 index bcdd3bc117..0000000000 --- a/internal/pkg/errorconcept/value_descriptors.go +++ /dev/null @@ -1,139 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 errorconcept - -import ( - "net/http" - - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" -) - -var ValueDescriptors valueDescriptorsErrorConcept - -// ValueDescriptorsErrorConcept represents the accessor for the value-descriptor-specific error concepts -type valueDescriptorsErrorConcept struct { - DuplicateName valueDescriptorDuplicateName - SingleInUse valueDescriptorInUse - MultipleInUse valueDescriptorsInUse - Invalid valueDescriptorInvalid - LimitExceeded valueDescriptorLimitExceeded - NotFound valueDescriptorNotFound - NotFoundInDB valueDescriptorDBNotFound -} - -type valueDescriptorDuplicateName struct{} - -func (r valueDescriptorDuplicateName) httpErrorCode() int { - return http.StatusConflict -} - -func (r valueDescriptorDuplicateName) isA(err error) bool { - _, ok := err.(errors.ErrDuplicateValueDescriptorName) - return ok -} - -func (r valueDescriptorDuplicateName) message(err error) string { - return err.Error() -} - -type valueDescriptorInUse struct{} - -func (r valueDescriptorInUse) httpErrorCode() int { - return http.StatusConflict -} - -func (r valueDescriptorInUse) isA(err error) bool { - _, ok := err.(errors.ErrValueDescriptorInUse) - return ok -} - -func (r valueDescriptorInUse) message(err error) string { - return err.Error() -} - -type valueDescriptorsInUse struct{} - -func (r valueDescriptorsInUse) httpErrorCode() int { - return http.StatusConflict -} - -func (r valueDescriptorsInUse) isA(err error) bool { - _, ok := err.(errors.ErrValueDescriptorsInUse) - return ok -} - -func (r valueDescriptorsInUse) message(err error) string { - return err.Error() -} - -type valueDescriptorInvalid struct{} - -func (r valueDescriptorInvalid) httpErrorCode() int { - return http.StatusBadRequest -} - -func (r valueDescriptorInvalid) isA(err error) bool { - _, ok := err.(errors.ErrValueDescriptorInvalid) - return ok -} - -func (r valueDescriptorInvalid) message(err error) string { - return err.Error() -} - -type valueDescriptorLimitExceeded struct{} - -func (r valueDescriptorLimitExceeded) httpErrorCode() int { - return http.StatusRequestEntityTooLarge -} - -func (r valueDescriptorLimitExceeded) isA(err error) bool { - _, ok := err.(errors.ErrLimitExceeded) - return ok -} - -func (r valueDescriptorLimitExceeded) message(err error) string { - return err.Error() -} - -type valueDescriptorNotFound struct{} - -func (r valueDescriptorNotFound) httpErrorCode() int { - return http.StatusNotFound -} - -func (r valueDescriptorNotFound) isA(err error) bool { - _, ok := err.(errors.ErrValueDescriptorNotFound) - return ok -} - -func (r valueDescriptorNotFound) message(err error) string { - return err.Error() -} - -type valueDescriptorDBNotFound struct{} - -func (r valueDescriptorDBNotFound) httpErrorCode() int { - return http.StatusConflict -} - -func (r valueDescriptorDBNotFound) isA(err error) bool { - _, ok := err.(errors.ErrDbNotFound) - return ok -} - -func (r valueDescriptorDBNotFound) message(err error) string { - return "Value descriptor not found for reading" -} diff --git a/internal/security/bootstrapper/main.go b/internal/security/bootstrapper/main.go index 100402417d..863657aa7a 100644 --- a/internal/security/bootstrapper/main.go +++ b/internal/security/bootstrapper/main.go @@ -22,7 +22,6 @@ import ( "os" "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/gorilla/mux" "github.com/edgexfoundry/edgex-go/internal" bootstrapper "github.com/edgexfoundry/edgex-go/internal/security/bootstrapper/command" @@ -42,7 +41,7 @@ const ( ) // Main function is the wrapper for the security bootstrapper main -func Main(ctx context.Context, cancel context.CancelFunc, _ *mux.Router, _ chan<- bool) { +func Main(ctx context.Context, cancel context.CancelFunc) { // service key for this bootstrapper service startupTimer := startup.NewStartUpTimer(clients.SecurityBootstrapperKey) diff --git a/internal/security/fileprovider/main.go b/internal/security/fileprovider/main.go index 98a4eca99b..66f4c690dd 100644 --- a/internal/security/fileprovider/main.go +++ b/internal/security/fileprovider/main.go @@ -30,11 +30,9 @@ import ( "github.com/edgexfoundry/go-mod-bootstrap/v2/di" "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - - "github.com/gorilla/mux" ) -func Main(ctx context.Context, cancel context.CancelFunc, _ *mux.Router, _ chan<- bool) { +func Main(ctx context.Context, cancel context.CancelFunc) { startupTimer := startup.NewStartUpTimer(clients.SecurityFileTokenProviderServiceKey) // All common command-line flags have been moved to DefaultCommonFlags. Service specific flags can be add here, diff --git a/internal/security/proxy/main.go b/internal/security/proxy/main.go index e70648d560..54962b5c84 100644 --- a/internal/security/proxy/main.go +++ b/internal/security/proxy/main.go @@ -30,11 +30,9 @@ import ( "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/startup" "github.com/edgexfoundry/go-mod-bootstrap/v2/di" "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - - "github.com/gorilla/mux" ) -func Main(ctx context.Context, cancel context.CancelFunc, _ *mux.Router, _ chan<- bool) { +func Main(ctx context.Context, cancel context.CancelFunc) { startupTimer := startup.NewStartUpTimer(clients.SecurityProxySetupServiceKey) var initNeeded bool diff --git a/internal/security/secretstore/main.go b/internal/security/secretstore/main.go index 9f35d9f354..deeb73928a 100644 --- a/internal/security/secretstore/main.go +++ b/internal/security/secretstore/main.go @@ -35,11 +35,9 @@ import ( "github.com/edgexfoundry/go-mod-bootstrap/v2/di" "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - - "github.com/gorilla/mux" ) -func Main(ctx context.Context, cancel context.CancelFunc, _ *mux.Router, _ chan<- bool) { +func Main(ctx context.Context, cancel context.CancelFunc) { startupTimer := startup.NewStartUpTimer(clients.SecuritySecretStoreSetupServiceKey) var insecureSkipVerify bool diff --git a/internal/support/notifications/cleanup.go b/internal/support/notifications/cleanup.go deleted file mode 100644 index da1855191f..0000000000 --- a/internal/support/notifications/cleanup.go +++ /dev/null @@ -1,76 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Technologies Inc. - * - * 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 notifications - -import ( - "net/http" - "strconv" - - "github.com/edgexfoundry/edgex-go/internal/support/notifications/interfaces" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - - "github.com/gorilla/mux" -) - -func cleanupHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - lc.Info("Cleaning up of notifications and transmissions") - cleanupHandlerCloser(w, dbClient.Cleanup(), lc) -} - -func cleanupAgeHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - vars := mux.Vars(r) - age, err := strconv.Atoi(vars["age"]) - // Problem converting age - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting the age to an integer") - return - } - - lc.Info("Cleaning up of notifications and transmissions") - cleanupHandlerCloser(w, dbClient.CleanupOld(age), lc) -} - -func cleanupHandlerCloser(w http.ResponseWriter, err error, lc logger.LoggingClient) { - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error(err.Error()) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusAccepted) - w.Write([]byte("true")) -} diff --git a/internal/support/notifications/const.go b/internal/support/notifications/const.go deleted file mode 100644 index 8e6989542b..0000000000 --- a/internal/support/notifications/const.go +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * Copyright 2018 Dell Technologies Inc. - * - * 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 notifications - -var ( - /* ----------------------- CONSTANTS ----------------------------*/ - ESCALATIONSUBSCRIPTIONSLUG = "ESCALATION" - ESCALATIONPREFIX = "escalated-" - ESCALATEDCONTENTNOTICE = "This notification is escalated by the transmission" - - /* ---------------- URL PARAM NAMES -----------------------*/ - START = "start" - END = "end" - LIMIT = "limit" - NOTIFICATION = "notification" - SUBSCRIPTION = "subscription" - TRANSMISSION = "transmission" - CLEANUP = "cleanup" - SLUG = "slug" - LABELS = "labels" - CATEGORIES = "categories" - ID = "id" - SENDER = "sender" - RECEIVER = "receiver" - AGE = "age" - NEW = "new" - ESCALATED = "escalated" - ACKNOWLEDGED = "acknowledged" - FAILED = "failed" - SENT = "sent" -) diff --git a/internal/support/notifications/distribution_coordinator.go b/internal/support/notifications/distribution_coordinator.go deleted file mode 100644 index a2fccf3ce4..0000000000 --- a/internal/support/notifications/distribution_coordinator.go +++ /dev/null @@ -1,76 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Technologies Inc. - * - * 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 notifications - -import ( - notificationsConfig "github.com/edgexfoundry/edgex-go/internal/support/notifications/config" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/interfaces" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -func distribute( - n models.Notification, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - config notificationsConfig.ConfigurationStruct) error { - - lc.Debug("DistributionCoordinator start distributing notification: " + n.Slug) - var categories []string - categories = append(categories, string(n.Category)) - subs, err := dbClient.GetSubscriptionByCategoriesLabels(categories, n.Labels) - if err != nil { - lc.Error("Unable to get subscriptions to distribute notification:" + n.Slug) - return err - } - for _, sub := range subs { - send(n, sub, lc, dbClient, config) - } - return nil -} - -func resend( - t models.Transmission, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - config notificationsConfig.ConfigurationStruct) { - - lc.Debug("Resending transmission: " + t.ID + " for: " + t.Notification.Slug) - resendViaChannel(t, lc, dbClient, config) -} - -func send( - n models.Notification, - s models.Subscription, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - config notificationsConfig.ConfigurationStruct) { - - for _, ch := range s.Channels { - sendViaChannel(n, ch, s.Receiver, lc, dbClient, config) - } -} - -func criticalSeverityResend( - t models.Transmission, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - config notificationsConfig.ConfigurationStruct) { - - lc.Info("Critical severity resend scheduler is triggered.") - resend(t, lc, dbClient, config) -} diff --git a/internal/support/notifications/errors/types.go b/internal/support/notifications/errors/types.go deleted file mode 100644 index 623bf12c30..0000000000 --- a/internal/support/notifications/errors/types.go +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 errors - -import ( - "fmt" - "strings" -) - -type ErrNotificationNotFound struct { - slug string -} - -func (e ErrNotificationNotFound) Error() string { - return fmt.Sprintf("Notification '%s' not found", e.slug) -} - -func NewErrNotificationNotFound(slug string) error { - return ErrNotificationNotFound{slug: slug} -} - -type ErrSubscriptionNotFound struct { - slug string -} - -func (e ErrSubscriptionNotFound) Error() string { - return fmt.Sprintf("Subscription '%s' not found", e.slug) -} - -func NewErrSubscriptionNotFound(slug string) error { - return ErrSubscriptionNotFound{slug: slug} -} - -type ErrInvalidEmailAddresses struct { - description string - addresses []string -} - -func (e ErrInvalidEmailAddresses) Error() string { - return fmt.Sprintf("Invalid email addresses [%s], Reason: %s", - strings.Join(e.addresses, ","), e.description) -} - -func NewErrInvalidEmailAddresses(addresses []string, description string) error { - return ErrInvalidEmailAddresses{description: description, - addresses: addresses} -} diff --git a/internal/support/notifications/escalating_service.go b/internal/support/notifications/escalating_service.go deleted file mode 100644 index 824d12eea7..0000000000 --- a/internal/support/notifications/escalating_service.go +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Technologies Inc. - * - * 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 notifications - -import ( - notificationsConfig "github.com/edgexfoundry/edgex-go/internal/support/notifications/config" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/interfaces" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -func escalate( - t models.Transmission, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - config notificationsConfig.ConfigurationStruct) { - - lc.Warn("Escalating transmission: " + t.ID + ", for: " + t.Notification.Slug) - - var err error - s, err := dbClient.GetSubscriptionBySlug(ESCALATIONSUBSCRIPTIONSLUG) - if err != nil { - lc.Error("Unable to find Escalation subscriber to send escalation notice for " + t.ID) - return - } - - n, err := createEscalatedNotification(t, dbClient) - if err != nil { - lc.Error("Unable to create new escalating notice to send escalation notice for " + t.ID) - return - } - - send(n, s, lc, dbClient, config) -} - -func createEscalatedNotification( - t models.Transmission, - dbClient interfaces.DBClient) (models.Notification, error) { - - old := t.Notification - n := models.Notification{Category: old.Category, Severity: old.Severity, Description: old.Description, Labels: old.Labels, ContentType: "text/plain"} - n.Slug = ESCALATIONPREFIX + old.Slug - n.Sender = ESCALATIONPREFIX + old.Sender - n.Content = ESCALATEDCONTENTNOTICE + " " + t.String() + " " + old.Content - n.Status = models.Escalated - _, err := dbClient.AddNotification(n) - return n, err -} diff --git a/internal/support/notifications/init.go b/internal/support/notifications/init.go index 335d9f13bf..9cb0cbc98a 100644 --- a/internal/support/notifications/init.go +++ b/internal/support/notifications/init.go @@ -43,7 +43,6 @@ func NewBootstrap(router *mux.Router) *Bootstrap { // BootstrapHandler fulfills the BootstrapHandler contract and performs initialization for the notifications service. func (b *Bootstrap) BootstrapHandler(_ context.Context, _ *sync.WaitGroup, _ startup.Timer, dic *di.Container) bool { - loadRestRoutes(b.router, dic) v2.LoadRestRoutes(b.router, dic) restSender := channel.NewRESTSender(dic) diff --git a/internal/support/notifications/interfaces/db.go b/internal/support/notifications/interfaces/db.go deleted file mode 100644 index 04c02cfb70..0000000000 --- a/internal/support/notifications/interfaces/db.go +++ /dev/null @@ -1,71 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Inc. - * - * 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 interfaces - -import ( - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type DBClient interface { - CloseSession() - - // Notifications - GetNotifications() ([]contract.Notification, error) - GetNotificationById(id string) (contract.Notification, error) - GetNotificationBySlug(slug string) (contract.Notification, error) - GetNotificationBySender(sender string, limit int) ([]contract.Notification, error) - GetNotificationsByLabels(labels []string, limit int) ([]contract.Notification, error) - GetNotificationsByStartEnd(start int64, end int64, limit int) ([]contract.Notification, error) - GetNotificationsByStart(start int64, limit int) ([]contract.Notification, error) - GetNotificationsByEnd(end int64, limit int) ([]contract.Notification, error) - GetNewNotifications(limit int) ([]contract.Notification, error) - GetNewNormalNotifications(limit int) ([]contract.Notification, error) - AddNotification(n contract.Notification) (string, error) - UpdateNotification(n contract.Notification) error - MarkNotificationProcessed(n contract.Notification) error - DeleteNotificationById(id string) error - DeleteNotificationBySlug(id string) error - DeleteNotificationsOld(age int) error - - // Subscriptions - GetSubscriptions() ([]contract.Subscription, error) - GetSubscriptionById(id string) (contract.Subscription, error) - GetSubscriptionBySlug(slug string) (contract.Subscription, error) - GetSubscriptionByReceiver(receiver string) ([]contract.Subscription, error) - GetSubscriptionByCategories(categories []string) ([]contract.Subscription, error) - GetSubscriptionByLabels(labels []string) ([]contract.Subscription, error) - GetSubscriptionByCategoriesLabels(categories []string, labels []string) ([]contract.Subscription, error) - AddSubscription(s contract.Subscription) (string, error) - UpdateSubscription(s contract.Subscription) error - DeleteSubscriptionById(id string) error - DeleteSubscriptionBySlug(id string) error - - // Transmissions - GetTransmissionById(id string) (contract.Transmission, error) - GetTransmissionsByNotificationSlug(slug string, limit int) ([]contract.Transmission, error) - GetTransmissionsByNotificationSlugAndStartEnd(slug string, start int64, end int64, limit int) ([]contract.Transmission, error) - GetTransmissionsByStartEnd(start int64, end int64, limit int) ([]contract.Transmission, error) - GetTransmissionsByStart(start int64, limit int) ([]contract.Transmission, error) - GetTransmissionsByEnd(end int64, limit int) ([]contract.Transmission, error) - GetTransmissionsByStatus(limit int, status contract.TransmissionStatus) ([]contract.Transmission, error) - AddTransmission(t contract.Transmission) (string, error) - UpdateTransmission(t contract.Transmission) error - DeleteTransmission(age int64, status contract.TransmissionStatus) error - - // General Cleanup - Cleanup() error - CleanupOld(age int) error -} diff --git a/internal/support/notifications/interfaces/mocks/DBClient.go b/internal/support/notifications/interfaces/mocks/DBClient.go deleted file mode 100644 index 3db3fc6c4d..0000000000 --- a/internal/support/notifications/interfaces/mocks/DBClient.go +++ /dev/null @@ -1,789 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// DBClient is an autogenerated mock type for the DBClient type -type DBClient struct { - mock.Mock -} - -// AddNotification provides a mock function with given fields: n -func (_m *DBClient) AddNotification(n models.Notification) (string, error) { - ret := _m.Called(n) - - var r0 string - if rf, ok := ret.Get(0).(func(models.Notification) string); ok { - r0 = rf(n) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.Notification) error); ok { - r1 = rf(n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AddSubscription provides a mock function with given fields: s -func (_m *DBClient) AddSubscription(s models.Subscription) (string, error) { - ret := _m.Called(s) - - var r0 string - if rf, ok := ret.Get(0).(func(models.Subscription) string); ok { - r0 = rf(s) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.Subscription) error); ok { - r1 = rf(s) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AddTransmission provides a mock function with given fields: t -func (_m *DBClient) AddTransmission(t models.Transmission) (string, error) { - ret := _m.Called(t) - - var r0 string - if rf, ok := ret.Get(0).(func(models.Transmission) string); ok { - r0 = rf(t) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.Transmission) error); ok { - r1 = rf(t) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Cleanup provides a mock function with given fields: -func (_m *DBClient) Cleanup() error { - ret := _m.Called() - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// CleanupOld provides a mock function with given fields: age -func (_m *DBClient) CleanupOld(age int) error { - ret := _m.Called(age) - - var r0 error - if rf, ok := ret.Get(0).(func(int) error); ok { - r0 = rf(age) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// CloseSession provides a mock function with given fields: -func (_m *DBClient) CloseSession() { - _m.Called() -} - -// DeleteNotificationById provides a mock function with given fields: id -func (_m *DBClient) DeleteNotificationById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteNotificationBySlug provides a mock function with given fields: id -func (_m *DBClient) DeleteNotificationBySlug(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteNotificationsOld provides a mock function with given fields: age -func (_m *DBClient) DeleteNotificationsOld(age int) error { - ret := _m.Called(age) - - var r0 error - if rf, ok := ret.Get(0).(func(int) error); ok { - r0 = rf(age) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteSubscriptionById provides a mock function with given fields: id -func (_m *DBClient) DeleteSubscriptionById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteSubscriptionBySlug provides a mock function with given fields: id -func (_m *DBClient) DeleteSubscriptionBySlug(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteTransmission provides a mock function with given fields: age, status -func (_m *DBClient) DeleteTransmission(age int64, status models.TransmissionStatus) error { - ret := _m.Called(age, status) - - var r0 error - if rf, ok := ret.Get(0).(func(int64, models.TransmissionStatus) error); ok { - r0 = rf(age, status) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// GetNewNormalNotifications provides a mock function with given fields: limit -func (_m *DBClient) GetNewNormalNotifications(limit int) ([]models.Notification, error) { - ret := _m.Called(limit) - - var r0 []models.Notification - if rf, ok := ret.Get(0).(func(int) []models.Notification); ok { - r0 = rf(limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Notification) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNewNotifications provides a mock function with given fields: limit -func (_m *DBClient) GetNewNotifications(limit int) ([]models.Notification, error) { - ret := _m.Called(limit) - - var r0 []models.Notification - if rf, ok := ret.Get(0).(func(int) []models.Notification); ok { - r0 = rf(limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Notification) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNotificationById provides a mock function with given fields: id -func (_m *DBClient) GetNotificationById(id string) (models.Notification, error) { - ret := _m.Called(id) - - var r0 models.Notification - if rf, ok := ret.Get(0).(func(string) models.Notification); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Notification) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNotificationBySender provides a mock function with given fields: sender, limit -func (_m *DBClient) GetNotificationBySender(sender string, limit int) ([]models.Notification, error) { - ret := _m.Called(sender, limit) - - var r0 []models.Notification - if rf, ok := ret.Get(0).(func(string, int) []models.Notification); ok { - r0 = rf(sender, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Notification) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, int) error); ok { - r1 = rf(sender, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNotificationBySlug provides a mock function with given fields: slug -func (_m *DBClient) GetNotificationBySlug(slug string) (models.Notification, error) { - ret := _m.Called(slug) - - var r0 models.Notification - if rf, ok := ret.Get(0).(func(string) models.Notification); ok { - r0 = rf(slug) - } else { - r0 = ret.Get(0).(models.Notification) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(slug) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNotifications provides a mock function with given fields: -func (_m *DBClient) GetNotifications() ([]models.Notification, error) { - ret := _m.Called() - - var r0 []models.Notification - if rf, ok := ret.Get(0).(func() []models.Notification); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Notification) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNotificationsByEnd provides a mock function with given fields: end, limit -func (_m *DBClient) GetNotificationsByEnd(end int64, limit int) ([]models.Notification, error) { - ret := _m.Called(end, limit) - - var r0 []models.Notification - if rf, ok := ret.Get(0).(func(int64, int) []models.Notification); ok { - r0 = rf(end, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Notification) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int64, int) error); ok { - r1 = rf(end, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNotificationsByLabels provides a mock function with given fields: labels, limit -func (_m *DBClient) GetNotificationsByLabels(labels []string, limit int) ([]models.Notification, error) { - ret := _m.Called(labels, limit) - - var r0 []models.Notification - if rf, ok := ret.Get(0).(func([]string, int) []models.Notification); ok { - r0 = rf(labels, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Notification) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func([]string, int) error); ok { - r1 = rf(labels, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNotificationsByStart provides a mock function with given fields: start, limit -func (_m *DBClient) GetNotificationsByStart(start int64, limit int) ([]models.Notification, error) { - ret := _m.Called(start, limit) - - var r0 []models.Notification - if rf, ok := ret.Get(0).(func(int64, int) []models.Notification); ok { - r0 = rf(start, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Notification) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int64, int) error); ok { - r1 = rf(start, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNotificationsByStartEnd provides a mock function with given fields: start, end, limit -func (_m *DBClient) GetNotificationsByStartEnd(start int64, end int64, limit int) ([]models.Notification, error) { - ret := _m.Called(start, end, limit) - - var r0 []models.Notification - if rf, ok := ret.Get(0).(func(int64, int64, int) []models.Notification); ok { - r0 = rf(start, end, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Notification) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int64, int64, int) error); ok { - r1 = rf(start, end, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSubscriptionByCategories provides a mock function with given fields: categories -func (_m *DBClient) GetSubscriptionByCategories(categories []string) ([]models.Subscription, error) { - ret := _m.Called(categories) - - var r0 []models.Subscription - if rf, ok := ret.Get(0).(func([]string) []models.Subscription); ok { - r0 = rf(categories) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Subscription) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func([]string) error); ok { - r1 = rf(categories) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSubscriptionByCategoriesLabels provides a mock function with given fields: categories, labels -func (_m *DBClient) GetSubscriptionByCategoriesLabels(categories []string, labels []string) ([]models.Subscription, error) { - ret := _m.Called(categories, labels) - - var r0 []models.Subscription - if rf, ok := ret.Get(0).(func([]string, []string) []models.Subscription); ok { - r0 = rf(categories, labels) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Subscription) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func([]string, []string) error); ok { - r1 = rf(categories, labels) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSubscriptionById provides a mock function with given fields: id -func (_m *DBClient) GetSubscriptionById(id string) (models.Subscription, error) { - ret := _m.Called(id) - - var r0 models.Subscription - if rf, ok := ret.Get(0).(func(string) models.Subscription); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Subscription) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSubscriptionByLabels provides a mock function with given fields: labels -func (_m *DBClient) GetSubscriptionByLabels(labels []string) ([]models.Subscription, error) { - ret := _m.Called(labels) - - var r0 []models.Subscription - if rf, ok := ret.Get(0).(func([]string) []models.Subscription); ok { - r0 = rf(labels) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Subscription) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func([]string) error); ok { - r1 = rf(labels) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSubscriptionByReceiver provides a mock function with given fields: receiver -func (_m *DBClient) GetSubscriptionByReceiver(receiver string) ([]models.Subscription, error) { - ret := _m.Called(receiver) - - var r0 []models.Subscription - if rf, ok := ret.Get(0).(func(string) []models.Subscription); ok { - r0 = rf(receiver) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Subscription) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(receiver) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSubscriptionBySlug provides a mock function with given fields: slug -func (_m *DBClient) GetSubscriptionBySlug(slug string) (models.Subscription, error) { - ret := _m.Called(slug) - - var r0 models.Subscription - if rf, ok := ret.Get(0).(func(string) models.Subscription); ok { - r0 = rf(slug) - } else { - r0 = ret.Get(0).(models.Subscription) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(slug) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSubscriptions provides a mock function with given fields: -func (_m *DBClient) GetSubscriptions() ([]models.Subscription, error) { - ret := _m.Called() - - var r0 []models.Subscription - if rf, ok := ret.Get(0).(func() []models.Subscription); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Subscription) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetTransmissionById provides a mock function with given fields: id -func (_m *DBClient) GetTransmissionById(id string) (models.Transmission, error) { - ret := _m.Called(id) - - var r0 models.Transmission - if rf, ok := ret.Get(0).(func(string) models.Transmission); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Transmission) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetTransmissionsByEnd provides a mock function with given fields: end, limit -func (_m *DBClient) GetTransmissionsByEnd(end int64, limit int) ([]models.Transmission, error) { - ret := _m.Called(end, limit) - - var r0 []models.Transmission - if rf, ok := ret.Get(0).(func(int64, int) []models.Transmission); ok { - r0 = rf(end, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Transmission) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int64, int) error); ok { - r1 = rf(end, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetTransmissionsByNotificationSlug provides a mock function with given fields: slug, limit -func (_m *DBClient) GetTransmissionsByNotificationSlug(slug string, limit int) ([]models.Transmission, error) { - ret := _m.Called(slug, limit) - - var r0 []models.Transmission - if rf, ok := ret.Get(0).(func(string, int) []models.Transmission); ok { - r0 = rf(slug, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Transmission) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, int) error); ok { - r1 = rf(slug, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetTransmissionsByNotificationSlugAndStartEnd provides a mock function with given fields: slug, start, end, limit -func (_m *DBClient) GetTransmissionsByNotificationSlugAndStartEnd(slug string, start int64, end int64, limit int) ([]models.Transmission, error) { - ret := _m.Called(slug, start, end, limit) - - var r0 []models.Transmission - if rf, ok := ret.Get(0).(func(string, int64, int64, int) []models.Transmission); ok { - r0 = rf(slug, start, end, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Transmission) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, int64, int64, int) error); ok { - r1 = rf(slug, start, end, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetTransmissionsByStart provides a mock function with given fields: start, limit -func (_m *DBClient) GetTransmissionsByStart(start int64, limit int) ([]models.Transmission, error) { - ret := _m.Called(start, limit) - - var r0 []models.Transmission - if rf, ok := ret.Get(0).(func(int64, int) []models.Transmission); ok { - r0 = rf(start, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Transmission) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int64, int) error); ok { - r1 = rf(start, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetTransmissionsByStartEnd provides a mock function with given fields: start, end, limit -func (_m *DBClient) GetTransmissionsByStartEnd(start int64, end int64, limit int) ([]models.Transmission, error) { - ret := _m.Called(start, end, limit) - - var r0 []models.Transmission - if rf, ok := ret.Get(0).(func(int64, int64, int) []models.Transmission); ok { - r0 = rf(start, end, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Transmission) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int64, int64, int) error); ok { - r1 = rf(start, end, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetTransmissionsByStatus provides a mock function with given fields: limit, status -func (_m *DBClient) GetTransmissionsByStatus(limit int, status models.TransmissionStatus) ([]models.Transmission, error) { - ret := _m.Called(limit, status) - - var r0 []models.Transmission - if rf, ok := ret.Get(0).(func(int, models.TransmissionStatus) []models.Transmission); ok { - r0 = rf(limit, status) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Transmission) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int, models.TransmissionStatus) error); ok { - r1 = rf(limit, status) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MarkNotificationProcessed provides a mock function with given fields: n -func (_m *DBClient) MarkNotificationProcessed(n models.Notification) error { - ret := _m.Called(n) - - var r0 error - if rf, ok := ret.Get(0).(func(models.Notification) error); ok { - r0 = rf(n) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateNotification provides a mock function with given fields: n -func (_m *DBClient) UpdateNotification(n models.Notification) error { - ret := _m.Called(n) - - var r0 error - if rf, ok := ret.Get(0).(func(models.Notification) error); ok { - r0 = rf(n) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateSubscription provides a mock function with given fields: s -func (_m *DBClient) UpdateSubscription(s models.Subscription) error { - ret := _m.Called(s) - - var r0 error - if rf, ok := ret.Get(0).(func(models.Subscription) error); ok { - r0 = rf(s) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateTransmission provides a mock function with given fields: t -func (_m *DBClient) UpdateTransmission(t models.Transmission) error { - ret := _m.Called(t) - - var r0 error - if rf, ok := ret.Get(0).(func(models.Transmission) error); ok { - r0 = rf(t) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/internal/support/notifications/main.go b/internal/support/notifications/main.go index 3708d10003..9ba84169c1 100644 --- a/internal/support/notifications/main.go +++ b/internal/support/notifications/main.go @@ -27,7 +27,6 @@ import ( "github.com/edgexfoundry/edgex-go" "github.com/edgexfoundry/edgex-go/internal" - "github.com/edgexfoundry/edgex-go/internal/pkg/bootstrap/handlers/database" "github.com/edgexfoundry/edgex-go/internal/pkg/telemetry" v2Handlers "github.com/edgexfoundry/edgex-go/internal/pkg/v2/bootstrap/handlers" notificationsConfig "github.com/edgexfoundry/edgex-go/internal/support/notifications/config" @@ -45,7 +44,7 @@ import ( "github.com/gorilla/mux" ) -func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router, readyStream chan<- bool) { +func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router) { startupTimer := startup.NewStartUpTimer(clients.SupportNotificationsServiceKey) // All common command-line flags have been moved to DefaultCommonFlags. Service specific flags can be add here, @@ -79,11 +78,9 @@ func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router, re true, []interfaces.BootstrapHandler{ v2Handlers.NewDatabase(httpServer, configuration, v2NotificationContainer.DBClientInterfaceName).BootstrapHandler, // add v2 db client bootstrap handler - database.NewDatabase(httpServer, configuration).BootstrapHandler, NewBootstrap(router).BootstrapHandler, telemetry.BootstrapHandler, httpServer.BootstrapHandler, handlers.NewStartMessage(clients.SupportNotificationsServiceKey, edgex.Version).BootstrapHandler, - handlers.NewReady(httpServer, readyStream).BootstrapHandler, }) } diff --git a/internal/support/notifications/normal_distribution.go b/internal/support/notifications/normal_distribution.go deleted file mode 100644 index 25498bdce2..0000000000 --- a/internal/support/notifications/normal_distribution.go +++ /dev/null @@ -1,40 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Technologies Inc. - * - * 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 notifications - -import ( - notificationsConfig "github.com/edgexfoundry/edgex-go/internal/support/notifications/config" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/interfaces" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -func distributeAndMark( - n models.Notification, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - config notificationsConfig.ConfigurationStruct) error { - - go distribute(n, lc, dbClient, config) - - err := dbClient.MarkNotificationProcessed(n) - if err != nil { - lc.Error("Trouble updating notification to Processed for: " + n.Slug) - return err - } - return nil -} diff --git a/internal/support/notifications/operators/notification/db.go b/internal/support/notifications/operators/notification/db.go deleted file mode 100644 index 5b8eccb1b9..0000000000 --- a/internal/support/notifications/operators/notification/db.go +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 notification - -import contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// NotificationLoader provides functionality for obtaining Notifications. -type NotificationLoader interface { - GetNotificationById(id string) (contract.Notification, error) - GetNotificationBySlug(slug string) (contract.Notification, error) - GetNotificationBySender(sender string, limit int) ([]contract.Notification, error) - GetNotificationsByStartEnd(start int64, end int64, limit int) ([]contract.Notification, error) - GetNotificationsByStart(start int64, limit int) ([]contract.Notification, error) - GetNotificationsByEnd(end int64, limit int) ([]contract.Notification, error) - GetNotificationsByLabels(labels []string, limit int) ([]contract.Notification, error) - GetNewNotifications(limit int) ([]contract.Notification, error) -} - -// NotificationDeleter deletes notifications. -type NotificationDeleter interface { - DeleteNotificationById(id string) error - DeleteNotificationBySlug(slug string) error - DeleteNotificationsOld(age int) error -} diff --git a/internal/support/notifications/operators/notification/delete.go b/internal/support/notifications/operators/notification/delete.go deleted file mode 100644 index a5825e170f..0000000000 --- a/internal/support/notifications/operators/notification/delete.go +++ /dev/null @@ -1,96 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 notification - -import ( - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/errors" -) - -// DeleteExecutor handles the deletion of a notification. -// Returns ErrNotificationNotFound if a notification could not be found with a matching ID -type DeleteExecutor interface { - Execute() error -} - -type deleteNotificationByID struct { - db NotificationDeleter - did string -} - -type deleteNotificationBySlug struct { - db NotificationDeleter - dslug string -} - -type deleteNotificationsByAge struct { - db NotificationDeleter - dage int -} - -// Execute performs the deletion of the notification. -func (dnbi deleteNotificationByID) Execute() error { - err := dnbi.db.DeleteNotificationById(dnbi.did) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrNotificationNotFound(dnbi.did) - } - return err - } - return nil -} - -func (dnbs deleteNotificationBySlug) Execute() error { - err := dnbs.db.DeleteNotificationBySlug(dnbs.dslug) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrNotificationNotFound(dnbs.dslug) - } - return err - } - return nil -} - -func (dnba deleteNotificationsByAge) Execute() error { - err := dnba.db.DeleteNotificationsOld(dnba.dage) - if err != nil { - return err - } - return nil -} - -// NewDeleteByIDExecutor creates a new DeleteExecutor which deletes a notifcation based on id. -func NewDeleteByIDExecutor(db NotificationDeleter, did string) DeleteExecutor { - return deleteNotificationByID{ - db: db, - did: did, - } -} - -// NewDeleteBySlugExecutor creates a new DeleteExecutor which deletes a notifcation based on slug. -func NewDeleteBySlugExecutor(db NotificationDeleter, dslug string) DeleteExecutor { - return deleteNotificationBySlug{ - db: db, - dslug: dslug, - } -} - -// NewDeleteBySlugExecutor creates a new DeleteExecutor which deletes a notifcation based on slug. -func NewDeleteByAgeExecutor(db NotificationDeleter, dage int) DeleteExecutor { - return deleteNotificationsByAge{ - db: db, - dage: dage, - } -} diff --git a/internal/support/notifications/operators/notification/delete_test.go b/internal/support/notifications/operators/notification/delete_test.go deleted file mode 100644 index 14f2afb574..0000000000 --- a/internal/support/notifications/operators/notification/delete_test.go +++ /dev/null @@ -1,187 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 notification - -import ( - "reflect" - "testing" - - notificationErrors "github.com/edgexfoundry/edgex-go/internal/support/notifications/errors" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/operators/notification/mocks" -) - -var TestAge int = 1564594093 - -func TestNotificationById(t *testing.T) { - tests := []struct { - name string - database NotificationDeleter - expectError bool - expectedErrorType error - }{ - { - name: "Successful Delete", - database: createMockNotificiationDeleterStringArg("DeleteNotificationById", nil, Id), - expectError: false, - expectedErrorType: nil, - }, - { - name: "Notification not found", - database: createMockNotificiationDeleterStringArg("DeleteNotificationById", ErrorNotFound, Id), - expectError: true, - expectedErrorType: notificationErrors.ErrNotificationNotFound{}, - }, - { - name: "Delete error", - database: createMockNotificiationDeleterStringArg("DeleteNotificationById", Error, Id), - expectError: true, - expectedErrorType: Error, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewDeleteByIDExecutor(test.database, Id) - err := op.Execute() - - if test.expectError && err == nil { - t.Error("We expected an error but did not get one") - } - - if !test.expectError && err != nil { - t.Errorf("We do not expected an error but got one. %s", err.Error()) - } - - if test.expectError { - eet := reflect.TypeOf(test.expectedErrorType) - aet := reflect.TypeOf(err) - if !aet.AssignableTo(eet) { - t.Errorf("Expected error of type %v, but got an error of type %v", eet, aet) - } - } - - return - }) - } -} - -func TestNotificationBySlug(t *testing.T) { - tests := []struct { - name string - database NotificationDeleter - expectError bool - expectedErrorType error - }{ - { - name: "Successful Delete", - database: createMockNotificiationDeleterStringArg("DeleteNotificationBySlug", nil, Slug), - expectError: false, - expectedErrorType: nil, - }, - { - name: "Notification not found", - database: createMockNotificiationDeleterStringArg("DeleteNotificationBySlug", ErrorNotFound, Slug), - expectError: true, - expectedErrorType: notificationErrors.ErrNotificationNotFound{}, - }, - { - name: "Delete error", - database: createMockNotificiationDeleterStringArg("DeleteNotificationBySlug", Error, Slug), - expectError: true, - expectedErrorType: Error, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewDeleteBySlugExecutor(test.database, Slug) - err := op.Execute() - - if test.expectError && err == nil { - t.Error("We expected an error but did not get one") - } - - if !test.expectError && err != nil { - t.Errorf("We do not expected an error but got one. %s", err.Error()) - } - - if test.expectError { - eet := reflect.TypeOf(test.expectedErrorType) - aet := reflect.TypeOf(err) - if !aet.AssignableTo(eet) { - t.Errorf("Expected error of type %v, but got an error of type %v", eet, aet) - } - } - - return - }) - } -} - -func createMockNotificiationDeleterStringArg(methodName string, err error, arg string) NotificationDeleter { - dbMock := mocks.NotificationDeleter{} - dbMock.On(methodName, arg).Return(err) - return &dbMock -} - -func createMockNotificiationDeleterIntArg(methodName string, err error, arg int) NotificationDeleter { - dbMock := mocks.NotificationDeleter{} - dbMock.On(methodName, arg).Return(err) - return &dbMock -} - -func TestNotificationsByAge(t *testing.T) { - tests := []struct { - name string - database NotificationDeleter - expectError bool - expectedErrorType error - }{ - { - name: "Successful Delete", - database: createMockNotificiationDeleterIntArg("DeleteNotificationsOld", nil, TestAge), - expectError: false, - expectedErrorType: nil, - }, - { - name: "Delete error", - database: createMockNotificiationDeleterIntArg("DeleteNotificationsOld", Error, TestAge), - expectError: true, - expectedErrorType: Error, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewDeleteByAgeExecutor(test.database, TestAge) - err := op.Execute() - - if test.expectError && err == nil { - t.Error("We expected an error but did not get one") - } - - if !test.expectError && err != nil { - t.Errorf("We do not expected an error but got one. %s", err.Error()) - } - - if test.expectError { - eet := reflect.TypeOf(test.expectedErrorType) - aet := reflect.TypeOf(err) - if !aet.AssignableTo(eet) { - t.Errorf("Expected error of type %v, but got an error of type %v", eet, aet) - } - } - - return - }) - } -} diff --git a/internal/support/notifications/operators/notification/get.go b/internal/support/notifications/operators/notification/get.go deleted file mode 100644 index e6fc602f8b..0000000000 --- a/internal/support/notifications/operators/notification/get.go +++ /dev/null @@ -1,225 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 notification - -import ( - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/errors" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type IdExecutor interface { - Execute() (contract.Notification, error) -} - -type CollectionExecutor interface { - Execute() ([]contract.Notification, error) -} - -type notificationLoadById struct { - database NotificationLoader - id string -} - -type notificationLoadBySlug struct { - database NotificationLoader - slug string -} - -type notificationsLoadBySender struct { - database NotificationLoader - limit int - sender string -} - -type notificationsLoadByStartEnd struct { - database NotificationLoader - limit int - start int64 - end int64 -} - -type notificationsLoadByStart struct { - database NotificationLoader - limit int - start int64 -} - -type notificationsLoadByEnd struct { - database NotificationLoader - limit int - end int64 -} - -type notificationsLoadByLabels struct { - database NotificationLoader - limit int - labels []string -} - -type notificationsLoadNew struct { - database NotificationLoader - limit int -} - -func (op notificationLoadById) Execute() (contract.Notification, error) { - res, err := op.database.GetNotificationById(op.id) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrNotificationNotFound(op.id) - } - return res, err - } - return res, nil -} - -func (op notificationLoadBySlug) Execute() (contract.Notification, error) { - res, err := op.database.GetNotificationBySlug(op.slug) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrNotificationNotFound(op.slug) - } - return res, err - } - return res, nil -} - -func (op notificationsLoadBySender) Execute() ([]contract.Notification, error) { - res, err := op.database.GetNotificationBySender(op.sender, op.limit) - if err != nil { - return res, err - } - if len(res) == 0 { - return res, db.ErrNotFound - } - return res, nil -} - -func (op notificationsLoadByStartEnd) Execute() ([]contract.Notification, error) { - res, err := op.database.GetNotificationsByStartEnd(op.start, op.end, op.limit) - if err != nil { - return res, err - } - if len(res) == 0 { - return res, db.ErrNotFound - } - return res, nil -} - -func (op notificationsLoadByStart) Execute() ([]contract.Notification, error) { - res, err := op.database.GetNotificationsByStart(op.start, op.limit) - if err != nil { - return res, err - } - if len(res) == 0 { - return res, db.ErrNotFound - } - return res, nil -} - -func (op notificationsLoadByEnd) Execute() ([]contract.Notification, error) { - res, err := op.database.GetNotificationsByEnd(op.end, op.limit) - if err != nil { - return res, err - } - if len(res) == 0 { - return res, db.ErrNotFound - } - return res, nil -} - -func (op notificationsLoadByLabels) Execute() ([]contract.Notification, error) { - n, err := op.database.GetNotificationsByLabels(op.labels, op.limit) - if err != nil { - return n, err - } - if len(n) == 0 { - return n, db.ErrNotFound - } - return n, nil -} - -func (op notificationsLoadNew) Execute() ([]contract.Notification, error) { - n, err := op.database.GetNewNotifications(op.limit) - if err != nil { - return n, err - } - if len(n) == 0 { - return n, db.ErrNotFound - } - return n, nil -} - -func NewIdExecutor(db NotificationLoader, id string) IdExecutor { - return notificationLoadById{ - database: db, - id: id, - } -} - -func NewSlugExecutor(db NotificationLoader, slug string) IdExecutor { - return notificationLoadBySlug{ - database: db, - slug: slug, - } -} - -func NewSenderExecutor(db NotificationLoader, sender string, limit int) CollectionExecutor { - return notificationsLoadBySender{ - database: db, - limit: limit, - sender: sender, - } -} - -func NewStartExecutor(db NotificationLoader, start int64, limit int) CollectionExecutor { - return notificationsLoadByStart{ - database: db, - limit: limit, - start: start, - } -} - -func NewEndExecutor(db NotificationLoader, end int64, limit int) CollectionExecutor { - return notificationsLoadByEnd{ - database: db, - limit: limit, - end: end, - } -} - -func NewStartEndExecutor(db NotificationLoader, start int64, end int64, limit int) CollectionExecutor { - return notificationsLoadByStartEnd{ - database: db, - limit: limit, - start: start, - end: end, - } -} - -func NewLabelsExecutor(db NotificationLoader, labels []string, limit int) CollectionExecutor { - return notificationsLoadByLabels{ - database: db, - limit: limit, - labels: labels, - } -} - -func NewGetNewestExecutor(db NotificationLoader, limit int) CollectionExecutor { - return notificationsLoadNew{ - database: db, - limit: limit, - } -} diff --git a/internal/support/notifications/operators/notification/get_test.go b/internal/support/notifications/operators/notification/get_test.go deleted file mode 100644 index 5e0afa040c..0000000000 --- a/internal/support/notifications/operators/notification/get_test.go +++ /dev/null @@ -1,572 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 notification - -import ( - "errors" - "reflect" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/operators/notification/mocks" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -var Id = "83cb038b-5a94-4707-985d-13effec62de2" -var Slug = "test-slug" -var Sender = "System Management" -var Limit = 5 -var Start int64 = 1564758450 -var End int64 = 1564758650 -var Labels = []string{ - "test_label", - "test_label2", -} -var Error = errors.New("test error") -var ErrorNotFound = db.ErrNotFound -var SuccessfulDatabaseResult = []contract.Notification{ - { - Slug: "notice-test-123", - Sender: "System Management", - Category: "SECURITY", - Severity: "CRITICAL", - Content: "Hello, Notification!", - Labels: Labels, - }, -} - -func TestIdExecutor(t *testing.T) { - tests := []struct { - name string - mockDb NotificationLoader - expectedResult contract.Notification - expectedError bool - }{ - { - name: "Successful database call", - mockDb: createMockNotificiationLoaderStringArg("GetNotificationById", nil, SuccessfulDatabaseResult[0], Id), - expectedResult: SuccessfulDatabaseResult[0], - expectedError: false, - }, - { - name: "Unsuccessful database call", - mockDb: createMockNotificiationLoaderStringArg("GetNotificationById", Error, contract.Notification{}, Id), - expectedResult: contract.Notification{}, - expectedError: true, - }, - { - name: "Notification not found", - mockDb: createMockNotificiationLoaderStringArg("GetNotificationById", ErrorNotFound, contract.Notification{}, Id), - expectedResult: contract.Notification{}, - expectedError: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewIdExecutor(test.mockDb, Id) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func TestSlugExecutor(t *testing.T) { - tests := []struct { - name string - mockDb NotificationLoader - expectedResult contract.Notification - expectedError bool - }{ - { - name: "Successful database call", - mockDb: createMockNotificiationLoaderStringArg("GetNotificationBySlug", nil, SuccessfulDatabaseResult[0], Id), - expectedResult: SuccessfulDatabaseResult[0], - expectedError: false, - }, - { - name: "Unsuccessful database call", - mockDb: createMockNotificiationLoaderStringArg("GetNotificationBySlug", Error, contract.Notification{}, Id), - expectedResult: contract.Notification{}, - expectedError: true, - }, - { - name: "Notification not found", - mockDb: createMockNotificiationLoaderStringArg("GetNotificationBySlug", ErrorNotFound, contract.Notification{}, Id), - expectedResult: contract.Notification{}, - expectedError: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewSlugExecutor(test.mockDb, Id) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func createMockNotificiationLoaderStringArg(methodName string, err error, ret interface{}, arg string) NotificationLoader { - dbMock := mocks.NotificationLoader{} - dbMock.On(methodName, arg).Return(ret, err) - return &dbMock -} - -func createMockNotificiationLoaderSenderStringArg(methodName string, err error, ret interface{}, sender string, limit int) NotificationLoader { - dbMock := mocks.NotificationLoader{} - dbMock.On(methodName, sender, limit).Return(ret, err) - return &dbMock -} - -func createMockNotificiationLoaderStartStringArg(methodName string, err error, ret interface{}, start int64, limit int) NotificationLoader { - dbMock := mocks.NotificationLoader{} - dbMock.On(methodName, start, limit).Return(ret, err) - return &dbMock -} - -func createMockNotificiationLoaderStartEndStringArg(methodName string, err error, ret interface{}, start int64, end int64, limit int) NotificationLoader { - dbMock := mocks.NotificationLoader{} - dbMock.On(methodName, start, end, limit).Return(ret, err) - return &dbMock -} - -func createMockNotificiationLoaderLabelsStringArg(methodName string, err error, ret interface{}, labels []string, limit int) NotificationLoader { - dbMock := mocks.NotificationLoader{} - dbMock.On(methodName, labels, limit).Return(ret, err) - return &dbMock -} - -func createMockNotificiationLoaderNewestStringArg(methodName string, err error, ret interface{}, limit int) NotificationLoader { - dbMock := mocks.NotificationLoader{} - dbMock.On(methodName, limit).Return(ret, err) - return &dbMock -} - -func TestSenderExecutor(t *testing.T) { - tests := []struct { - name string - mockDb NotificationLoader - expectedResult []contract.Notification - expectedError bool - expectedErrType error - }{ - { - name: "Successful database call", - mockDb: createMockNotificiationLoaderSenderStringArg("GetNotificationBySender", nil, SuccessfulDatabaseResult, Sender, Limit), - expectedResult: SuccessfulDatabaseResult, - expectedError: false, - expectedErrType: nil, - }, - { - name: "Unsuccessful database call", - mockDb: createMockNotificiationLoaderSenderStringArg("GetNotificationBySender", Error, []contract.Notification{}, Sender, Limit), - expectedResult: []contract.Notification{}, - expectedError: true, - expectedErrType: Error, - }, - { - name: "Notification not found", - mockDb: createMockNotificiationLoaderSenderStringArg("GetNotificationBySender", nil, []contract.Notification{}, Sender, Limit), - expectedResult: []contract.Notification{}, - expectedError: true, - expectedErrType: ErrorNotFound, - }, - { - name: "Unknown Error", - mockDb: createMockNotificiationLoaderSenderStringArg("GetNotificationBySender", Error, SuccessfulDatabaseResult, Sender, Limit), - expectedResult: SuccessfulDatabaseResult, - expectedError: true, - expectedErrType: Error, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewSenderExecutor(test.mockDb, Sender, Limit) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedErrType, err) { - t.Errorf("Expected error result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedErrType, err) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func TestStartExecutor(t *testing.T) { - tests := []struct { - name string - mockDb NotificationLoader - expectedResult []contract.Notification - expectedError bool - expectedErrType error - }{ - { - name: "Successful database call", - mockDb: createMockNotificiationLoaderStartStringArg("GetNotificationsByStart", nil, SuccessfulDatabaseResult, Start, Limit), - expectedResult: SuccessfulDatabaseResult, - expectedError: false, - expectedErrType: nil, - }, - { - name: "Unsuccessful database call", - mockDb: createMockNotificiationLoaderStartStringArg("GetNotificationsByStart", Error, []contract.Notification{}, Start, Limit), - expectedResult: []contract.Notification{}, - expectedError: true, - expectedErrType: Error, - }, - { - name: "Notification not found", - mockDb: createMockNotificiationLoaderStartStringArg("GetNotificationsByStart", nil, []contract.Notification{}, Start, Limit), - expectedResult: []contract.Notification{}, - expectedError: true, - expectedErrType: ErrorNotFound, - }, - { - name: "Unknown Error", - mockDb: createMockNotificiationLoaderStartStringArg("GetNotificationsByStart", Error, SuccessfulDatabaseResult, Start, Limit), - expectedResult: SuccessfulDatabaseResult, - expectedError: true, - expectedErrType: Error, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewStartExecutor(test.mockDb, Start, Limit) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedErrType, err) { - t.Errorf("Expected error result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedErrType, err) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func TestEndExecutor(t *testing.T) { - tests := []struct { - name string - mockDb NotificationLoader - expectedResult []contract.Notification - expectedError bool - expectedErrType error - }{ - { - name: "Successful database call", - mockDb: createMockNotificiationLoaderStartStringArg("GetNotificationsByEnd", nil, SuccessfulDatabaseResult, End, Limit), - expectedResult: SuccessfulDatabaseResult, - expectedError: false, - expectedErrType: nil, - }, - { - name: "Unsuccessful database call", - mockDb: createMockNotificiationLoaderStartStringArg("GetNotificationsByEnd", Error, []contract.Notification{}, End, Limit), - expectedResult: []contract.Notification{}, - expectedError: true, - expectedErrType: Error, - }, - { - name: "Notification not found", - mockDb: createMockNotificiationLoaderStartStringArg("GetNotificationsByEnd", nil, []contract.Notification{}, End, Limit), - expectedResult: []contract.Notification{}, - expectedError: true, - expectedErrType: ErrorNotFound, - }, - { - name: "Unknown Error", - mockDb: createMockNotificiationLoaderStartStringArg("GetNotificationsByEnd", Error, SuccessfulDatabaseResult, End, Limit), - expectedResult: SuccessfulDatabaseResult, - expectedError: true, - expectedErrType: Error, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewEndExecutor(test.mockDb, End, Limit) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedErrType, err) { - t.Errorf("Expected error result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedErrType, err) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func TestStartEndExecutor(t *testing.T) { - tests := []struct { - name string - mockDb NotificationLoader - expectedResult []contract.Notification - expectedError bool - expectedErrType error - }{ - { - name: "Successful database call", - mockDb: createMockNotificiationLoaderStartEndStringArg("GetNotificationsByStartEnd", nil, SuccessfulDatabaseResult, Start, End, Limit), - expectedResult: SuccessfulDatabaseResult, - expectedError: false, - expectedErrType: nil, - }, - { - name: "Unsuccessful database call", - mockDb: createMockNotificiationLoaderStartEndStringArg("GetNotificationsByStartEnd", Error, []contract.Notification{}, Start, End, Limit), - expectedResult: []contract.Notification{}, - expectedError: true, - expectedErrType: Error, - }, - { - name: "Notification not found", - mockDb: createMockNotificiationLoaderStartEndStringArg("GetNotificationsByStartEnd", nil, []contract.Notification{}, Start, End, Limit), - expectedResult: []contract.Notification{}, - expectedError: true, - expectedErrType: ErrorNotFound, - }, - { - name: "Unknown Error", - mockDb: createMockNotificiationLoaderStartEndStringArg("GetNotificationsByStartEnd", Error, SuccessfulDatabaseResult, Start, End, Limit), - expectedResult: SuccessfulDatabaseResult, - expectedError: true, - expectedErrType: Error, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewStartEndExecutor(test.mockDb, Start, End, Limit) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedErrType, err) { - t.Errorf("Expected error result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedErrType, err) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func TestLabelsExecutor(t *testing.T) { - tests := []struct { - name string - mockDb NotificationLoader - expectedResult []contract.Notification - expectedError bool - expectedErrType error - }{ - { - name: "Successful database call", - mockDb: createMockNotificiationLoaderLabelsStringArg("GetNotificationsByLabels", nil, SuccessfulDatabaseResult, Labels, Limit), - expectedResult: SuccessfulDatabaseResult, - expectedError: false, - expectedErrType: nil, - }, - { - name: "Unsuccessful database call", - mockDb: createMockNotificiationLoaderLabelsStringArg("GetNotificationsByLabels", Error, []contract.Notification{}, Labels, Limit), - expectedResult: []contract.Notification{}, - expectedError: true, - expectedErrType: Error, - }, - { - name: "Notification not found", - mockDb: createMockNotificiationLoaderLabelsStringArg("GetNotificationsByLabels", nil, []contract.Notification{}, Labels, Limit), - expectedResult: []contract.Notification{}, - expectedError: true, - expectedErrType: ErrorNotFound, - }, - { - name: "Unknown Error", - mockDb: createMockNotificiationLoaderLabelsStringArg("GetNotificationsByLabels", Error, SuccessfulDatabaseResult, Labels, Limit), - expectedResult: SuccessfulDatabaseResult, - expectedError: true, - expectedErrType: Error, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewLabelsExecutor(test.mockDb, Labels, Limit) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedErrType, err) { - t.Errorf("Expected error result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedErrType, err) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func TestNewGetNewestExecutor(t *testing.T) { - tests := []struct { - name string - mockDb NotificationLoader - expectedResult []contract.Notification - expectedError bool - expectedErrType error - }{ - { - name: "Successful database call", - mockDb: createMockNotificiationLoaderNewestStringArg("GetNewNotifications", nil, SuccessfulDatabaseResult, Limit), - expectedResult: SuccessfulDatabaseResult, - expectedError: false, - expectedErrType: nil, - }, - { - name: "Unsuccessful database call", - mockDb: createMockNotificiationLoaderNewestStringArg("GetNewNotifications", Error, []contract.Notification{}, Limit), - expectedResult: []contract.Notification{}, - expectedError: true, - expectedErrType: Error, - }, - { - name: "Notification not found", - mockDb: createMockNotificiationLoaderNewestStringArg("GetNewNotifications", nil, []contract.Notification{}, Limit), - expectedResult: []contract.Notification{}, - expectedError: true, - expectedErrType: ErrorNotFound, - }, - { - name: "Unknown Error", - mockDb: createMockNotificiationLoaderNewestStringArg("GetNewNotifications", Error, SuccessfulDatabaseResult, Limit), - expectedResult: SuccessfulDatabaseResult, - expectedError: true, - expectedErrType: Error, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewGetNewestExecutor(test.mockDb, Limit) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedErrType, err) { - t.Errorf("Expected error result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedErrType, err) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} diff --git a/internal/support/notifications/operators/notification/mocks/NotificationDeleter.go b/internal/support/notifications/operators/notification/mocks/NotificationDeleter.go deleted file mode 100644 index 72a29f9638..0000000000 --- a/internal/support/notifications/operators/notification/mocks/NotificationDeleter.go +++ /dev/null @@ -1,52 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" - -// NotificationDeleter is an autogenerated mock type for the NotificationDeleter type -type NotificationDeleter struct { - mock.Mock -} - -// DeleteNotificationById provides a mock function with given fields: id -func (_m *NotificationDeleter) DeleteNotificationById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteNotificationBySlug provides a mock function with given fields: slug -func (_m *NotificationDeleter) DeleteNotificationBySlug(slug string) error { - ret := _m.Called(slug) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(slug) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteNotificationsOld provides a mock function with given fields: age -func (_m *NotificationDeleter) DeleteNotificationsOld(age int) error { - ret := _m.Called(age) - - var r0 error - if rf, ok := ret.Get(0).(func(int) error); ok { - r0 = rf(age) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/internal/support/notifications/operators/notification/mocks/NotificationLoader.go b/internal/support/notifications/operators/notification/mocks/NotificationLoader.go deleted file mode 100644 index ce2d28e748..0000000000 --- a/internal/support/notifications/operators/notification/mocks/NotificationLoader.go +++ /dev/null @@ -1,191 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// NotificationLoader is an autogenerated mock type for the NotificationLoader type -type NotificationLoader struct { - mock.Mock -} - -// GetNewNotifications provides a mock function with given fields: limit -func (_m *NotificationLoader) GetNewNotifications(limit int) ([]models.Notification, error) { - ret := _m.Called(limit) - - var r0 []models.Notification - if rf, ok := ret.Get(0).(func(int) []models.Notification); ok { - r0 = rf(limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Notification) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNotificationById provides a mock function with given fields: id -func (_m *NotificationLoader) GetNotificationById(id string) (models.Notification, error) { - ret := _m.Called(id) - - var r0 models.Notification - if rf, ok := ret.Get(0).(func(string) models.Notification); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Notification) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNotificationBySender provides a mock function with given fields: sender, limit -func (_m *NotificationLoader) GetNotificationBySender(sender string, limit int) ([]models.Notification, error) { - ret := _m.Called(sender, limit) - - var r0 []models.Notification - if rf, ok := ret.Get(0).(func(string, int) []models.Notification); ok { - r0 = rf(sender, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Notification) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, int) error); ok { - r1 = rf(sender, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNotificationBySlug provides a mock function with given fields: slug -func (_m *NotificationLoader) GetNotificationBySlug(slug string) (models.Notification, error) { - ret := _m.Called(slug) - - var r0 models.Notification - if rf, ok := ret.Get(0).(func(string) models.Notification); ok { - r0 = rf(slug) - } else { - r0 = ret.Get(0).(models.Notification) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(slug) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNotificationsByEnd provides a mock function with given fields: end, limit -func (_m *NotificationLoader) GetNotificationsByEnd(end int64, limit int) ([]models.Notification, error) { - ret := _m.Called(end, limit) - - var r0 []models.Notification - if rf, ok := ret.Get(0).(func(int64, int) []models.Notification); ok { - r0 = rf(end, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Notification) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int64, int) error); ok { - r1 = rf(end, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNotificationsByLabels provides a mock function with given fields: labels, limit -func (_m *NotificationLoader) GetNotificationsByLabels(labels []string, limit int) ([]models.Notification, error) { - ret := _m.Called(labels, limit) - - var r0 []models.Notification - if rf, ok := ret.Get(0).(func([]string, int) []models.Notification); ok { - r0 = rf(labels, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Notification) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func([]string, int) error); ok { - r1 = rf(labels, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNotificationsByStart provides a mock function with given fields: start, limit -func (_m *NotificationLoader) GetNotificationsByStart(start int64, limit int) ([]models.Notification, error) { - ret := _m.Called(start, limit) - - var r0 []models.Notification - if rf, ok := ret.Get(0).(func(int64, int) []models.Notification); ok { - r0 = rf(start, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Notification) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int64, int) error); ok { - r1 = rf(start, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNotificationsByStartEnd provides a mock function with given fields: start, end, limit -func (_m *NotificationLoader) GetNotificationsByStartEnd(start int64, end int64, limit int) ([]models.Notification, error) { - ret := _m.Called(start, end, limit) - - var r0 []models.Notification - if rf, ok := ret.Get(0).(func(int64, int64, int) []models.Notification); ok { - r0 = rf(start, end, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Notification) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int64, int64, int) error); ok { - r1 = rf(start, end, limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/support/notifications/operators/subscription/add.go b/internal/support/notifications/operators/subscription/add.go deleted file mode 100644 index f2b3a6d9bb..0000000000 --- a/internal/support/notifications/operators/subscription/add.go +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 subscription - -import ( - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type AddExecutor interface { - Execute() error -} - -type subscriptionAdd struct { - database SubscriptionWriter - subscription models.Subscription -} - -func (op subscriptionAdd) Execute() error { - _, err := op.database.AddSubscription(op.subscription) - if err != nil { - return err - } - return nil -} - -func NewAddExecutor(database SubscriptionWriter, subscription models.Subscription) AddExecutor { - return subscriptionAdd{ - database: database, - subscription: subscription, - } -} diff --git a/internal/support/notifications/operators/subscription/add_test.go b/internal/support/notifications/operators/subscription/add_test.go deleted file mode 100644 index 6313195521..0000000000 --- a/internal/support/notifications/operators/subscription/add_test.go +++ /dev/null @@ -1,87 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 subscription - -import ( - "testing" - - "github.com/edgexfoundry/edgex-go/internal/support/notifications/operators/subscription/mocks" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -var ValidSubscription = SuccessfulDatabaseResult[0] - -func TestAddExecutor(t *testing.T) { - - tests := []struct { - name string - mockDb SubscriptionWriter - subscription contract.Subscription - expectedResult string - expectedError bool - expectedErrorVal error - }{ - - { - name: "Successful database call", - mockDb: createAddMockSubscriptionSuccess(), - subscription: ValidSubscription, - expectedResult: ValidSubscription.ID, - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "Error", - mockDb: createAddMockSubscriptionErr(), - subscription: ValidSubscription, - expectedResult: "", - expectedError: true, - expectedErrorVal: Error, - }, - } - - for _, test := range tests { - t.Run(test.name, func(tt *testing.T) { - op := NewAddExecutor(test.mockDb, test.subscription) - err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -func createAddMockSubscriptionSuccess() SubscriptionWriter { - dbMock := mocks.SubscriptionWriter{} - dbMock.On("AddSubscription", ValidSubscription).Return(Id, nil) - return &dbMock -} - -func createAddMockSubscriptionErr() SubscriptionWriter { - dbMock := mocks.SubscriptionWriter{} - dbMock.On("AddSubscription", ValidSubscription).Return(Id, Error) - return &dbMock -} diff --git a/internal/support/notifications/operators/subscription/db.go b/internal/support/notifications/operators/subscription/db.go deleted file mode 100644 index 74ca4ded09..0000000000 --- a/internal/support/notifications/operators/subscription/db.go +++ /dev/null @@ -1,42 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 subscription - -import contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// SubscriptionLoader provides functionality for obtaining Subscriptions. -type SubscriptionLoader interface { - GetSubscriptions() ([]contract.Subscription, error) - GetSubscriptionById(id string) (contract.Subscription, error) - GetSubscriptionBySlug(slug string) (contract.Subscription, error) - GetSubscriptionByCategories(categories []string) ([]contract.Subscription, error) -} - -// SubscriptionWriter adds subscriptions. -type SubscriptionWriter interface { - AddSubscription(s contract.Subscription) (string, error) -} - -// SubscriptionDeleter deletes subscriptions. -type SubscriptionDeleter interface { - DeleteSubscriptionById(id string) error - DeleteSubscriptionBySlug(id string) error -} - -// SubscriptionUpdater updates subscriptions. -type SubscriptionUpdater interface { - UpdateSubscription(s contract.Subscription) error - SubscriptionLoader -} diff --git a/internal/support/notifications/operators/subscription/delete.go b/internal/support/notifications/operators/subscription/delete.go deleted file mode 100644 index ed4847d742..0000000000 --- a/internal/support/notifications/operators/subscription/delete.go +++ /dev/null @@ -1,74 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 subscription - -import ( - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/errors" -) - -// DeleteExecutor handles the deletion of a subscription. -// Returns ErrNSubscriptionNotFound if a subscription could not be found with a matching ID -type DeleteExecutor interface { - Execute() error -} - -type deleteSubscriptionByID struct { - db SubscriptionDeleter - did string -} - -type deleteSubscriptionBySlug struct { - db SubscriptionDeleter - dslug string -} - -func (dnbi deleteSubscriptionByID) Execute() error { - err := dnbi.db.DeleteSubscriptionById(dnbi.did) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrSubscriptionNotFound(dnbi.did) - } - return err - } - return nil -} - -func (dnbi deleteSubscriptionBySlug) Execute() error { - err := dnbi.db.DeleteSubscriptionBySlug(dnbi.dslug) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrSubscriptionNotFound(dnbi.dslug) - } - return err - } - return nil -} - -// NewDeleteByIDExecutor creates a new DeleteExecutor which deletes a subscription based on id. -func NewDeleteByIDExecutor(db SubscriptionDeleter, did string) DeleteExecutor { - return deleteSubscriptionByID{ - db: db, - did: did, - } -} - -// NewDeleteBySlugExecutor creates a new DeleteExecutor which deletes a subscription based on slug. -func NewDeleteBySlugExecutor(db SubscriptionDeleter, dslug string) DeleteExecutor { - return deleteSubscriptionBySlug{ - db: db, - dslug: dslug, - } -} diff --git a/internal/support/notifications/operators/subscription/delete_test.go b/internal/support/notifications/operators/subscription/delete_test.go deleted file mode 100644 index a0871d2982..0000000000 --- a/internal/support/notifications/operators/subscription/delete_test.go +++ /dev/null @@ -1,133 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 subscription - -import ( - "reflect" - "testing" - - notificationErrors "github.com/edgexfoundry/edgex-go/internal/support/notifications/errors" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/operators/subscription/mocks" -) - -func TestSubscriptionById(t *testing.T) { - tests := []struct { - name string - database SubscriptionDeleter - expectError bool - expectedErrorType error - }{ - { - name: "Successful Delete", - database: createMockSubscriptionDeleterStringArg("DeleteSubscriptionById", nil, Id), - expectError: false, - expectedErrorType: nil, - }, - { - name: "Subscription not found", - database: createMockSubscriptionDeleterStringArg("DeleteSubscriptionById", ErrorNotFound, Id), - expectError: true, - expectedErrorType: notificationErrors.ErrSubscriptionNotFound{}, - }, - { - name: "Delete error", - database: createMockSubscriptionDeleterStringArg("DeleteSubscriptionById", Error, Id), - expectError: true, - expectedErrorType: Error, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewDeleteByIDExecutor(test.database, Id) - err := op.Execute() - - if test.expectError && err == nil { - t.Error("We expected an error but did not get one") - } - - if !test.expectError && err != nil { - t.Errorf("We do not expected an error but got one. %s", err.Error()) - } - - if test.expectError { - eet := reflect.TypeOf(test.expectedErrorType) - aet := reflect.TypeOf(err) - if !aet.AssignableTo(eet) { - t.Errorf("Expected error of type %v, but got an error of type %v", eet, aet) - } - } - - return - }) - } -} - -func TestSubscriptionBySlug(t *testing.T) { - tests := []struct { - name string - database SubscriptionDeleter - expectError bool - expectedErrorType error - }{ - { - name: "Successful Delete", - database: createMockSubscriptionDeleterStringArg("DeleteSubscriptionBySlug", nil, Slug), - expectError: false, - expectedErrorType: nil, - }, - { - name: "Subscription not found", - database: createMockSubscriptionDeleterStringArg("DeleteSubscriptionBySlug", ErrorNotFound, Slug), - expectError: true, - expectedErrorType: notificationErrors.ErrSubscriptionNotFound{}, - }, - { - name: "Delete error", - database: createMockSubscriptionDeleterStringArg("DeleteSubscriptionBySlug", Error, Slug), - expectError: true, - expectedErrorType: Error, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewDeleteBySlugExecutor(test.database, Slug) - err := op.Execute() - - if test.expectError && err == nil { - t.Error("We expected an error but did not get one") - } - - if !test.expectError && err != nil { - t.Errorf("We do not expected an error but got one. %s", err.Error()) - } - - if test.expectError { - eet := reflect.TypeOf(test.expectedErrorType) - aet := reflect.TypeOf(err) - if !aet.AssignableTo(eet) { - t.Errorf("Expected error of type %v, but got an error of type %v", eet, aet) - } - } - - return - }) - } -} - -func createMockSubscriptionDeleterStringArg(methodName string, err error, arg string) SubscriptionDeleter { - dbMock := mocks.SubscriptionDeleter{} - dbMock.On(methodName, arg).Return(err) - return &dbMock -} diff --git a/internal/support/notifications/operators/subscription/get.go b/internal/support/notifications/operators/subscription/get.go deleted file mode 100644 index c78bc33046..0000000000 --- a/internal/support/notifications/operators/subscription/get.go +++ /dev/null @@ -1,117 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 subscription - -import ( - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/errors" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type IdExecutor interface { - Execute() (contract.Subscription, error) -} - -type CollectionExecutor interface { - Execute() ([]contract.Subscription, error) -} - -type subscriptionsAll struct { - database SubscriptionLoader -} - -type subscriptionLoadById struct { - database SubscriptionLoader - id string -} - -type subscriptionLoadBySlug struct { - database SubscriptionLoader - slug string -} - -type subscriptionsLoadByCategories struct { - database SubscriptionLoader - categories []string -} - -func (op subscriptionsAll) Execute() ([]contract.Subscription, error) { - subscriptions, err := op.database.GetSubscriptions() - if err != nil { - return nil, err - } - return subscriptions, nil -} - -func (op subscriptionLoadById) Execute() (contract.Subscription, error) { - res, err := op.database.GetSubscriptionById(op.id) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrSubscriptionNotFound(op.id) - } - return res, err - } - return res, nil -} - -func (op subscriptionLoadBySlug) Execute() (contract.Subscription, error) { - s, err := op.database.GetSubscriptionBySlug(op.slug) - if err != nil { - if err == db.ErrNotFound { - newErr := errors.NewErrSubscriptionNotFound(op.slug) - return s, newErr - } - return s, err - } - return s, nil -} - -func (op subscriptionsLoadByCategories) Execute() ([]contract.Subscription, error) { - s, err := op.database.GetSubscriptionByCategories(op.categories) - if err != nil { - return s, err - } - if len(s) == 0 { - return s, db.ErrNotFound - } - return s, nil -} - -func NewAllExecutor(db SubscriptionLoader) CollectionExecutor { - return subscriptionsAll{ - database: db, - } -} - -func NewIdExecutor(db SubscriptionLoader, id string) IdExecutor { - return subscriptionLoadById{ - database: db, - id: id, - } -} - -func NewSlugExecutor(db SubscriptionLoader, slug string) IdExecutor { - return subscriptionLoadBySlug{ - database: db, - slug: slug, - } -} - -func NewCategoriesExecutor(db SubscriptionLoader, categories []string) CollectionExecutor { - return subscriptionsLoadByCategories{ - database: db, - categories: categories, - } -} diff --git a/internal/support/notifications/operators/subscription/get_test.go b/internal/support/notifications/operators/subscription/get_test.go deleted file mode 100644 index 7367ebf75e..0000000000 --- a/internal/support/notifications/operators/subscription/get_test.go +++ /dev/null @@ -1,280 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 subscription - -import ( - "errors" - "reflect" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/operators/subscription/mocks" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -const ( - Id = "83cb038b-5a94-4707-985d-13effec62de2" - Slug = "test" -) - -var Categories = []string{ - "Test Category", -} - -var Error = errors.New("test error") -var ErrorNotFound = db.ErrNotFound -var SuccessfulDatabaseResult = []contract.Subscription{ - { - Slug: "notice-test-123", - Receiver: "System Admin", - SubscribedCategories: []contract.NotificationsCategory{ - "SECURITY", - "HW_HEALTH", - "SW_HEALTH", - }, - SubscribedLabels: []string{ - "Dell", - "IoT", - "test", - }, - Channels: []contract.Channel{ - { - Type: "REST", - Url: "http://abc.def/alert", - }, - { - Type: "EMAIL", - MailAddresses: []string{ - "cloud@abc.def", - "jack@abc.def", - }, - }, - }, - }, -} - -func TestAllExecutor(t *testing.T) { - tests := []struct { - name string - mockDb SubscriptionLoader - expectedResult []contract.Subscription - expectedError bool - }{ - { - name: "Successful database call", - mockDb: createMockSubscriptionLoaderAll("GetSubscriptions", nil, SuccessfulDatabaseResult), - expectedResult: SuccessfulDatabaseResult, - expectedError: false, - }, - { - name: "Unsuccessful database call", - mockDb: createMockSubscriptionLoaderAll("GetSubscriptions", Error, nil), - expectedResult: nil, - expectedError: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewAllExecutor(test.mockDb) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func TestIdExecutor(t *testing.T) { - tests := []struct { - name string - mockDb SubscriptionLoader - expectedResult contract.Subscription - expectedError bool - }{ - { - name: "Successful database call", - mockDb: createMockSubscriptionLoaderStringArg("GetSubscriptionById", nil, SuccessfulDatabaseResult[0], Id), - expectedResult: SuccessfulDatabaseResult[0], - expectedError: false, - }, - { - name: "Unsuccessful database call", - mockDb: createMockSubscriptionLoaderStringArg("GetSubscriptionById", Error, contract.Subscription{}, Id), - expectedResult: contract.Subscription{}, - expectedError: true, - }, - { - name: "Subscription not found", - mockDb: createMockSubscriptionLoaderStringArg("GetSubscriptionById", ErrorNotFound, contract.Subscription{}, Id), - expectedResult: contract.Subscription{}, - expectedError: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewIdExecutor(test.mockDb, Id) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func createMockSubscriptionLoaderStringArg(methodName string, err error, ret interface{}, arg string) SubscriptionLoader { - dbMock := mocks.SubscriptionLoader{} - dbMock.On(methodName, arg).Return(ret, err) - return &dbMock -} - -func createMockSubscriptionLoaderAll(methodName string, err error, ret []contract.Subscription) SubscriptionLoader { - dbMock := mocks.SubscriptionLoader{} - dbMock.On(methodName).Return(ret, err) - return &dbMock -} - -func createMockSubscriptionLoaderByCategories(methodName string, err error, ret []contract.Subscription, arg []string) SubscriptionLoader { - dbMock := mocks.SubscriptionLoader{} - dbMock.On(methodName, arg).Return(ret, err) - return &dbMock -} - -func TestSlugExecutor(t *testing.T) { - tests := []struct { - name string - mockDb SubscriptionLoader - expectedResult contract.Subscription - expectedError bool - }{ - { - name: "Successful database call", - mockDb: createMockSubscriptionLoaderStringArg("GetSubscriptionBySlug", nil, SuccessfulDatabaseResult[0], Slug), - expectedResult: SuccessfulDatabaseResult[0], - expectedError: false, - }, - { - name: "Unsuccessful database call", - mockDb: createMockSubscriptionLoaderStringArg("GetSubscriptionBySlug", Error, contract.Subscription{}, Slug), - expectedResult: contract.Subscription{}, - expectedError: true, - }, - { - name: "Subscription not found", - mockDb: createMockSubscriptionLoaderStringArg("GetSubscriptionBySlug", ErrorNotFound, contract.Subscription{}, Slug), - expectedResult: contract.Subscription{}, - expectedError: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewSlugExecutor(test.mockDb, Slug) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func TestCategoriesExecutor(t *testing.T) { - tests := []struct { - name string - mockDb SubscriptionLoader - expectedResult []contract.Subscription - expectedError bool - }{ - { - name: "Subscription not found", - mockDb: createMockSubscriptionLoaderByCategories("GetSubscriptionByCategories", ErrorNotFound, []contract.Subscription{}, Categories), - expectedResult: []contract.Subscription{}, - expectedError: true, - }, - { - name: "Successful database call", - mockDb: createMockSubscriptionLoaderByCategories("GetSubscriptionByCategories", nil, SuccessfulDatabaseResult, Categories), - expectedResult: SuccessfulDatabaseResult, - expectedError: false, - }, - { - name: "Unsuccessful database call", - mockDb: createMockSubscriptionLoaderByCategories("GetSubscriptionByCategories", Error, []contract.Subscription{}, Categories), - expectedResult: []contract.Subscription{}, - expectedError: true, - }, - { - name: "Subscription not found", - mockDb: createMockSubscriptionLoaderByCategories("GetSubscriptionByCategories", nil, []contract.Subscription{}, Categories), - expectedResult: []contract.Subscription{}, - expectedError: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewCategoriesExecutor(test.mockDb, Categories) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} diff --git a/internal/support/notifications/operators/subscription/mocks/SubscriptionDeleter.go b/internal/support/notifications/operators/subscription/mocks/SubscriptionDeleter.go deleted file mode 100644 index f7fc23645a..0000000000 --- a/internal/support/notifications/operators/subscription/mocks/SubscriptionDeleter.go +++ /dev/null @@ -1,38 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" - -// SubscriptionDeleter is an autogenerated mock type for the SubscriptionDeleter type -type SubscriptionDeleter struct { - mock.Mock -} - -// DeleteSubscriptionById provides a mock function with given fields: id -func (_m *SubscriptionDeleter) DeleteSubscriptionById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteSubscriptionBySlug provides a mock function with given fields: id -func (_m *SubscriptionDeleter) DeleteSubscriptionBySlug(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/internal/support/notifications/operators/subscription/mocks/SubscriptionLoader.go b/internal/support/notifications/operators/subscription/mocks/SubscriptionLoader.go deleted file mode 100644 index 5811ce3ec2..0000000000 --- a/internal/support/notifications/operators/subscription/mocks/SubscriptionLoader.go +++ /dev/null @@ -1,99 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// SubscriptionLoader is an autogenerated mock type for the SubscriptionLoader type -type SubscriptionLoader struct { - mock.Mock -} - -// GetSubscriptionByCategories provides a mock function with given fields: categories -func (_m *SubscriptionLoader) GetSubscriptionByCategories(categories []string) ([]models.Subscription, error) { - ret := _m.Called(categories) - - var r0 []models.Subscription - if rf, ok := ret.Get(0).(func([]string) []models.Subscription); ok { - r0 = rf(categories) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Subscription) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func([]string) error); ok { - r1 = rf(categories) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSubscriptionById provides a mock function with given fields: id -func (_m *SubscriptionLoader) GetSubscriptionById(id string) (models.Subscription, error) { - ret := _m.Called(id) - - var r0 models.Subscription - if rf, ok := ret.Get(0).(func(string) models.Subscription); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Subscription) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSubscriptionBySlug provides a mock function with given fields: slug -func (_m *SubscriptionLoader) GetSubscriptionBySlug(slug string) (models.Subscription, error) { - ret := _m.Called(slug) - - var r0 models.Subscription - if rf, ok := ret.Get(0).(func(string) models.Subscription); ok { - r0 = rf(slug) - } else { - r0 = ret.Get(0).(models.Subscription) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(slug) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSubscriptions provides a mock function with given fields: -func (_m *SubscriptionLoader) GetSubscriptions() ([]models.Subscription, error) { - ret := _m.Called() - - var r0 []models.Subscription - if rf, ok := ret.Get(0).(func() []models.Subscription); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Subscription) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/support/notifications/operators/subscription/mocks/SubscriptionUpdater.go b/internal/support/notifications/operators/subscription/mocks/SubscriptionUpdater.go deleted file mode 100644 index 9f4fe25f02..0000000000 --- a/internal/support/notifications/operators/subscription/mocks/SubscriptionUpdater.go +++ /dev/null @@ -1,113 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// SubscriptionUpdater is an autogenerated mock type for the SubscriptionUpdater type -type SubscriptionUpdater struct { - mock.Mock -} - -// GetSubscriptionByCategories provides a mock function with given fields: categories -func (_m *SubscriptionUpdater) GetSubscriptionByCategories(categories []string) ([]models.Subscription, error) { - ret := _m.Called(categories) - - var r0 []models.Subscription - if rf, ok := ret.Get(0).(func([]string) []models.Subscription); ok { - r0 = rf(categories) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Subscription) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func([]string) error); ok { - r1 = rf(categories) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSubscriptionById provides a mock function with given fields: id -func (_m *SubscriptionUpdater) GetSubscriptionById(id string) (models.Subscription, error) { - ret := _m.Called(id) - - var r0 models.Subscription - if rf, ok := ret.Get(0).(func(string) models.Subscription); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Subscription) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSubscriptionBySlug provides a mock function with given fields: slug -func (_m *SubscriptionUpdater) GetSubscriptionBySlug(slug string) (models.Subscription, error) { - ret := _m.Called(slug) - - var r0 models.Subscription - if rf, ok := ret.Get(0).(func(string) models.Subscription); ok { - r0 = rf(slug) - } else { - r0 = ret.Get(0).(models.Subscription) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(slug) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSubscriptions provides a mock function with given fields: -func (_m *SubscriptionUpdater) GetSubscriptions() ([]models.Subscription, error) { - ret := _m.Called() - - var r0 []models.Subscription - if rf, ok := ret.Get(0).(func() []models.Subscription); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Subscription) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateSubscription provides a mock function with given fields: s -func (_m *SubscriptionUpdater) UpdateSubscription(s models.Subscription) error { - ret := _m.Called(s) - - var r0 error - if rf, ok := ret.Get(0).(func(models.Subscription) error); ok { - r0 = rf(s) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/internal/support/notifications/operators/subscription/mocks/SubscriptionWriter.go b/internal/support/notifications/operators/subscription/mocks/SubscriptionWriter.go deleted file mode 100644 index f81c938439..0000000000 --- a/internal/support/notifications/operators/subscription/mocks/SubscriptionWriter.go +++ /dev/null @@ -1,32 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// SubscriptionWriter is an autogenerated mock type for the SubscriptionWriter type -type SubscriptionWriter struct { - mock.Mock -} - -// AddSubscription provides a mock function with given fields: s -func (_m *SubscriptionWriter) AddSubscription(s models.Subscription) (string, error) { - ret := _m.Called(s) - - var r0 string - if rf, ok := ret.Get(0).(func(models.Subscription) string); ok { - r0 = rf(s) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.Subscription) error); ok { - r1 = rf(s) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/support/notifications/operators/subscription/update.go b/internal/support/notifications/operators/subscription/update.go deleted file mode 100644 index 2504c83a02..0000000000 --- a/internal/support/notifications/operators/subscription/update.go +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 subscription - -import ( - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type UpdateExecutor interface { - Execute() error -} - -type subscriptionUpdate struct { - database SubscriptionUpdater - subscription models.Subscription -} - -func (op subscriptionUpdate) Execute() error { - // Check if the subscription exists - s2, err := op.database.GetSubscriptionBySlug(op.subscription.Slug) - if err != nil { - return err - } else { - op.subscription.ID = s2.ID - } - - if err = op.database.UpdateSubscription(op.subscription); err != nil { - return err - } - return nil -} - -func NewUpdateExecutor(database SubscriptionUpdater, subscription models.Subscription) UpdateExecutor { - return subscriptionUpdate{ - database: database, - subscription: subscription, - } -} diff --git a/internal/support/notifications/operators/subscription/update_test.go b/internal/support/notifications/operators/subscription/update_test.go deleted file mode 100644 index 80eeee6569..0000000000 --- a/internal/support/notifications/operators/subscription/update_test.go +++ /dev/null @@ -1,103 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 subscription - -import ( - "testing" - - "github.com/edgexfoundry/edgex-go/internal/support/notifications/operators/subscription/mocks" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -func TestUpdateExecutor(t *testing.T) { - - tests := []struct { - name string - mockDb SubscriptionUpdater - subscription contract.Subscription - expectedResult string - expectedError bool - expectedErrorVal error - }{ - - { - name: "Successful database call", - mockDb: createUpdateMockSubscriptionSuccess(), - subscription: ValidSubscription, - expectedResult: ValidSubscription.ID, - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "GetSubscriptionBySlug Error", - mockDb: createUpdateMockSubscriptionGetErr(), - subscription: ValidSubscription, - expectedResult: "", - expectedError: true, - expectedErrorVal: Error, - }, - { - name: "Error", - mockDb: createUpdateMockSubscriptionErr(), - subscription: ValidSubscription, - expectedResult: "", - expectedError: true, - expectedErrorVal: Error, - }, - } - - for _, test := range tests { - t.Run(test.name, func(tt *testing.T) { - op := NewUpdateExecutor(test.mockDb, test.subscription) - err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -func createUpdateMockSubscriptionSuccess() SubscriptionUpdater { - dbMock := mocks.SubscriptionUpdater{} - dbMock.On("GetSubscriptionBySlug", ValidSubscription.Slug).Return(ValidSubscription, nil) - dbMock.On("UpdateSubscription", ValidSubscription).Return(nil) - return &dbMock -} - -func createUpdateMockSubscriptionGetErr() SubscriptionUpdater { - dbMock := mocks.SubscriptionUpdater{} - dbMock.On("GetSubscriptionBySlug", ValidSubscription.Slug).Return(ValidSubscription, Error) - dbMock.On("UpdateSubscription", ValidSubscription).Return(Error) - return &dbMock -} - -func createUpdateMockSubscriptionErr() SubscriptionUpdater { - dbMock := mocks.SubscriptionUpdater{} - dbMock.On("GetSubscriptionBySlug", ValidSubscription.Slug).Return(ValidSubscription, nil) - dbMock.On("UpdateSubscription", ValidSubscription).Return(Error) - return &dbMock -} diff --git a/internal/support/notifications/rest_notification.go b/internal/support/notifications/rest_notification.go deleted file mode 100644 index d2b968a451..0000000000 --- a/internal/support/notifications/rest_notification.go +++ /dev/null @@ -1,505 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Technologies Inc. - * - * 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 notifications - -import ( - "encoding/json" - "net/http" - "strconv" - - "github.com/edgexfoundry/edgex-go/internal/pkg" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - notificationsConfig "github.com/edgexfoundry/edgex-go/internal/support/notifications/config" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/errors" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/interfaces" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/operators/notification" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" -) - -func notificationHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - config notificationsConfig.ConfigurationStruct) { - - if r.Body != nil { - defer r.Body.Close() - } - - var n models.Notification - dec := json.NewDecoder(r.Body) - err := dec.Decode(&n) - - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error("Error decoding notification: " + err.Error()) - return - } - - lc.Info("Posting Notification: " + n.String()) - n.Status = models.NotificationsStatus(models.New) - n.ID, err = dbClient.AddNotification(n) - if err != nil { - http.Error(w, err.Error(), http.StatusConflict) - lc.Error(err.Error()) - return - } - - lc.Debug("The scheduler is triggered for: " + n.Slug) - n, err = dbClient.GetNotificationById(n.ID) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error(err.Error()) - return - } - - err = distributeAndMark(n, lc, dbClient, config) - if err != nil { - return - } - lc.Debug("The scheduler has completed for: " + n.Slug) - - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - w.WriteHeader(http.StatusAccepted) - w.Write([]byte(n.ID)) - -} - -func restGetNotificationBySlug( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - slug := vars["slug"] - - op := notification.NewSlugExecutor(dbClient, slug) - result, err := op.Execute() - if err != nil { - lc.Error(err.Error()) - switch err.(type) { - case errors.ErrNotificationNotFound: - http.Error(w, err.Error(), http.StatusNotFound) - default: - - http.Error(w, err.Error(), http.StatusInternalServerError) - } - return - } - - pkg.Encode(result, w, lc) -} - -func restDeleteNotificationBySlug( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - slug := vars["slug"] - - lc.Info("Deleting notification (and associated transmissions) by slug: " + slug) - - op := notification.NewDeleteBySlugExecutor(dbClient, slug) - err := op.Execute() - if err != nil { - lc.Error(err.Error()) - switch err.(type) { - case errors.ErrNotificationNotFound: - http.Error(w, err.Error(), http.StatusNotFound) - default: - - http.Error(w, err.Error(), http.StatusInternalServerError) - } - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restGetNotificationByID( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - var id string = vars["id"] - op := notification.NewIdExecutor(dbClient, id) - result, err := op.Execute() - if err != nil { - lc.Error(err.Error()) - switch err.(type) { - case errors.ErrNotificationNotFound: - http.Error(w, err.Error(), http.StatusNotFound) - default: - - http.Error(w, err.Error(), http.StatusInternalServerError) - } - return - } - pkg.Encode(result, w, lc) -} - -func restDeleteNotificationByID( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - id := vars["id"] - - lc.Info("Deleting notification (and associated transmissions): " + id) - - op := notification.NewDeleteByIDExecutor(dbClient, id) - err := op.Execute() - - if err != nil { - lc.Error(err.Error()) - switch err.(type) { - case errors.ErrNotificationNotFound: - http.Error(w, err.Error(), http.StatusNotFound) - default: - http.Error(w, err.Error(), http.StatusInternalServerError) - } - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restDeleteNotificationsByAge( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - age, err := strconv.Atoi(vars["age"]) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting the age to an integer") - return - } - lc.Info("Deleting old notifications (and associated transmissions): " + vars["age"]) - op := notification.NewDeleteByAgeExecutor(dbClient, age) - err = op.Execute() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error(err.Error()) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restGetNotificationsBySender( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - config notificationsConfig.ConfigurationStruct) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - limitNum, err := strconv.Atoi(vars["limit"]) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting limit to integer: " + err.Error()) - return - } - - // Check the length - if err = checkMaxLimit(limitNum, lc, config); err != nil { - http.Error(w, ExceededMaxResultCount, http.StatusRequestEntityTooLarge) - return - } - - op := notification.NewSenderExecutor(dbClient, vars["sender"], limitNum) - results, err := op.Execute() - if err != nil { - if err == db.ErrNotFound { - http.Error(w, "Notification not found", http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - pkg.Encode(results, w, lc) -} - -func restNotificationByStartEnd( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - config notificationsConfig.ConfigurationStruct) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - start, err := strconv.ParseInt(vars["start"], 10, 64) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting the start to an integer") - return - } - end, err := strconv.ParseInt(vars["end"], 10, 64) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting the end to an integer") - return - } - limitNum, err := strconv.Atoi(vars["limit"]) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting limit to integer: " + err.Error()) - return - } - - // Check the length - if err = checkMaxLimit(limitNum, lc, config); err != nil { - http.Error(w, ExceededMaxResultCount, http.StatusRequestEntityTooLarge) - return - } - - op := notification.NewStartEndExecutor(dbClient, start, end, limitNum) - results, err := op.Execute() - if err != nil { - if err == db.ErrNotFound { - http.Error(w, "Notification not found", http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - pkg.Encode(results, w, lc) -} - -func restNotificationByStart( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - config notificationsConfig.ConfigurationStruct) { - - if r.Body != nil { - defer r.Body.Close() - } - vars := mux.Vars(r) - start, err := strconv.ParseInt(vars["start"], 10, 64) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting the start to an integer") - return - } - limitNum, err := strconv.Atoi(vars["limit"]) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting limit to integer: " + err.Error()) - return - } - - // Check the length - if err = checkMaxLimit(limitNum, lc, config); err != nil { - http.Error(w, ExceededMaxResultCount, http.StatusRequestEntityTooLarge) - return - } - - op := notification.NewStartExecutor(dbClient, start, limitNum) - results, err := op.Execute() - if err != nil { - if err == db.ErrNotFound { - http.Error(w, "Notification not found", http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - pkg.Encode(results, w, lc) -} - -func restNotificationByEnd( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - config notificationsConfig.ConfigurationStruct) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - end, err := strconv.ParseInt(vars["end"], 10, 64) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting the end to an integer") - return - } - limitNum, err := strconv.Atoi(vars["limit"]) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting limit to integer: " + err.Error()) - return - } - - // Check the length - if err = checkMaxLimit(limitNum, lc, config); err != nil { - http.Error(w, ExceededMaxResultCount, http.StatusRequestEntityTooLarge) - return - } - - op := notification.NewEndExecutor(dbClient, end, limitNum) - results, err := op.Execute() - if err != nil { - if err == db.ErrNotFound { - http.Error(w, "Notification not found", http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - pkg.Encode(results, w, lc) -} - -func restNotificationsByLabels( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - config notificationsConfig.ConfigurationStruct) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - limitNum, err := strconv.Atoi(vars["limit"]) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting limit to integer: " + err.Error()) - return - } - - // Check the length - if err = checkMaxLimit(limitNum, lc, config); err != nil { - http.Error(w, ExceededMaxResultCount, http.StatusRequestEntityTooLarge) - return - } - - labels := splitVars(vars["labels"]) - - op := notification.NewLabelsExecutor(dbClient, labels, limitNum) - results, err := op.Execute() - if err != nil { - if err == db.ErrNotFound { - http.Error(w, "Notification not found", http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - pkg.Encode(results, w, lc) -} - -func restNotificationsNew( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - config notificationsConfig.ConfigurationStruct) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - limitNum, err := strconv.Atoi(vars["limit"]) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting limit to integer: " + err.Error()) - return - } - - // Check the length - if err = checkMaxLimit(limitNum, lc, config); err != nil { - http.Error(w, ExceededMaxResultCount, http.StatusRequestEntityTooLarge) - return - } - - op := notification.NewGetNewestExecutor(dbClient, limitNum) - n, err := op.Execute() - if err != nil { - if err == db.ErrNotFound { - http.Error(w, "Notification not found", http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - pkg.Encode(n, w, lc) -} diff --git a/internal/support/notifications/rest_notification_test.go b/internal/support/notifications/rest_notification_test.go deleted file mode 100644 index ad55f3b6b4..0000000000 --- a/internal/support/notifications/rest_notification_test.go +++ /dev/null @@ -1,1016 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 notifications - -import ( - "bytes" - "encoding/json" - "errors" - "net/http" - "net/http/httptest" - "strconv" - "strings" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - notificationsConfig "github.com/edgexfoundry/edgex-go/internal/support/notifications/config" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/interfaces" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/interfaces/mocks" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" -) - -// TestURI this is not really used since we are using the HTTP testing framework and not creating routes, but rather -// creating a specific handler which will accept all requests. Therefore, the URI is not important. -var TestURI = "/notification" -var TestId = "123e4567-e89b-12d3-a456-426655440000" -var TestSlug = "test-slug" -var TestAge = 1564594093 -var TestLimit = 5 -var TestTooLargeLimit = 100 -var TestInvalidLimit = "invalid-limit" -var TestInvalidAge = "invalid age" -var TestSender = "System Management" -var TestStart int64 = 1564758450 -var TestEnd int64 = 1564758650 -var notificationId = "526c5c28-7a21-48a8-90f6-8009400441f4" -var invalidNotificationId = "..." -var badNotificationId = "11111111-7a21-48a8-90f6-8009400441f4" -var notificationServiceURI = clients.ApiBase + "/" + NOTIFICATIONSERVICE -var testError = errors.New("some error") - -var TestLabels = []string{ - "test_label", - "test_label2", -} - -var TestCategories = []string{ - "test_category", -} - -const ( - NOTIFICATIONSERVICE = "notification" -) - -func TestGetNotificationById(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createRequest(map[string]string{ID: TestId}), - createMockNotificationLoader("GetNotificationById", TestId, nil), - http.StatusOK, - }, - { - name: "Notification not found", - request: createRequest(map[string]string{ID: TestId}), - dbMock: createMockNotificationLoader("GetNotificationById", TestId, db.ErrNotFound), - expectedStatus: http.StatusNotFound, - }, - { - name: "Other error from database", - request: createRequest(map[string]string{ID: TestId}), - dbMock: createMockNotificationLoader("GetNotificationById", TestId, errors.New("Test error")), - expectedStatus: http.StatusInternalServerError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetNotificationByID(rr, tt.request, logger.NewMockClient(), tt.dbMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestGetNotificationBySlug(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createRequest(map[string]string{SLUG: TestSlug}), - createMockNotificationLoader("GetNotificationBySlug", TestSlug, nil), - http.StatusOK, - }, - { - name: "Notification not found", - request: createRequest(map[string]string{SLUG: TestSlug}), - dbMock: createMockNotificationLoader("GetNotificationBySlug", TestSlug, db.ErrNotFound), - expectedStatus: http.StatusNotFound, - }, - { - name: "Other error from database", - request: createRequest(map[string]string{SLUG: TestSlug}), - dbMock: createMockNotificationLoader("GetNotificationBySlug", TestSlug, errors.New("Test error")), - expectedStatus: http.StatusInternalServerError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetNotificationBySlug(rr, tt.request, logger.NewMockClient(), tt.dbMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func createMockNotificationLoader(methodName string, testID string, desiredError error) interfaces.DBClient { - myMock := mocks.DBClient{} - - if desiredError != nil { - myMock.On(methodName, testID).Return(contract.Notification{}, desiredError) - } else { - myMock.On(methodName, testID).Return(createNotifications(1)[0], nil) - } - return &myMock -} - -func createMockNotificationDeleter(methodName string, testID string, desiredError error) interfaces.DBClient { - myMock := mocks.DBClient{} - - if desiredError != nil { - myMock.On(methodName, testID).Return(desiredError) - } else { - myMock.On(methodName, testID).Return(nil) - } - return &myMock -} - -func createMockNotificationAgeDeleter(methodName string, testAge int, desiredError error) interfaces.DBClient { - myMock := mocks.DBClient{} - - if desiredError != nil { - myMock.On(methodName, testAge).Return(desiredError) - } else { - myMock.On(methodName, testAge).Return(nil) - } - return &myMock -} - -func createMockNotificationSenderLoader(methodName string, sender string, limit int, desiredError error, ret interface{}) interfaces.DBClient { - myMock := mocks.DBClient{} - - if desiredError != nil { - myMock.On(methodName, sender, limit).Return(ret, desiredError) - } else { - myMock.On(methodName, sender, limit).Return(ret, nil) - } - return &myMock -} - -func createMockNotificationStartLoader(methodName string, start int64, limit int, desiredError error, ret interface{}) interfaces.DBClient { - myMock := mocks.DBClient{} - - if desiredError != nil { - myMock.On(methodName, start, limit).Return(ret, desiredError) - } else { - myMock.On(methodName, start, limit).Return(ret, nil) - } - return &myMock -} - -func createMockNotificationStartEndLoader(methodName string, start int64, end int64, limit int, desiredError error, ret interface{}) interfaces.DBClient { - myMock := mocks.DBClient{} - - if desiredError != nil { - myMock.On(methodName, start, end, limit).Return(ret, desiredError) - } else { - myMock.On(methodName, start, end, limit).Return(ret, nil) - } - return &myMock -} - -func createMockNotificationLabelsLoader(methodName string, labels []string, limit int, desiredError error, ret interface{}) interfaces.DBClient { - myMock := mocks.DBClient{} - - if desiredError != nil { - myMock.On(methodName, labels, limit).Return(ret, desiredError) - } else { - myMock.On(methodName, labels, limit).Return(ret, nil) - } - return &myMock -} - -func createMockNotificationNewestLoader(methodName string, limit int, desiredError error, ret interface{}) interfaces.DBClient { - myMock := mocks.DBClient{} - - if desiredError != nil { - myMock.On(methodName, limit).Return(ret, desiredError) - } else { - myMock.On(methodName, limit).Return(ret, nil) - } - return &myMock -} - -func createRequest(params map[string]string) *http.Request { - req := httptest.NewRequest(http.MethodGet, TestURI, nil) - return mux.SetURLVars(req, params) -} - -func createDeleteRequest(params map[string]string) *http.Request { - req := httptest.NewRequest(http.MethodDelete, TestURI, nil) - return mux.SetURLVars(req, params) -} - -func createNotifications(howMany int) []contract.Notification { - var notifications []contract.Notification - for i := 0; i < howMany; i++ { - notifications = append(notifications, contract.Notification{ - Slug: "notice-test-123", - Sender: "System Management", - Category: "SECURITY", - Severity: "CRITICAL", - Content: "Hello, Notification!", - Labels: []string{ - "cool", - "test", - }, - }) - } - return notifications -} - -func createNotificationBySeverityLevel(severityLevel string) contract.Notification { - var notification contract.Notification - - switch severityLevel { - case contract.Critical: - notification = contract.Notification{ - ID: notificationId, - Slug: "notice-critical-123", - Sender: "Sender A", - Category: "SECURITY", - Severity: contract.Critical, - Content: "Hello, Notification!", - Status: "NEW", - Labels: []string{ - "first-label", - "second-label", - }, - } - case contract.Normal: - notification = contract.Notification{ - ID: notificationId, - Slug: "notice-normal-123", - Sender: "Sender B", - Category: "SECURITY", - Severity: contract.Normal, - Content: "Hello, Notification!", - Status: "NEW", - Labels: []string{ - "first-label", - "second-label", - }, - } - default: - // ... - } - - return notification -} - -func createInvalidNotification() contract.Notification { - var notification contract.Notification - - notification = contract.Notification{ - ID: invalidNotificationId, - Slug: "...", - Sender: "...", - Category: "...", - Severity: contract.Critical, - Content: "...", - Status: "...", - Labels: []string{ - "...", - "...", - }, - } - return notification -} - -func createInvalidCategoriesAndLabelsNotification() contract.Notification { - var notification contract.Notification - - notification = contract.Notification{ - ID: notificationId, - Slug: "notice-critical-123", - Sender: "Sender A", - Category: "...", - Severity: contract.Critical, - Content: "Hello, Notification!", - Status: "NEW", - Labels: []string{ - "first-bad-label", - "second-bad-label", - }, - } - return notification -} - -func createBadNotification() contract.Notification { - var notification contract.Notification - - notification = contract.Notification{ - ID: badNotificationId, - Slug: "notice-normal-123", - Sender: "Sender B", - Category: "SECURITY", - Severity: contract.Critical, - Content: "Hello, Notification!", - Status: "NEW", - Labels: []string{ - "first-label", - "second-label", - }, - } - return notification -} - -// This function serves to update the unexported isValidated field (in "go-mod-core-contracts"), -// which can only be done by marshalling and unmarshalling to JSON. -func validateNotification(notification *contract.Notification) contract.Notification { - b, _ := json.Marshal(notification) - _ = notification.UnmarshalJSON(b) - return *notification -} - -func createNotificationHandlerRequestWithBody( - httpMethod string, - notification contract.Notification, - pathParams map[string]string) *http.Request { - - // if your JSON marshalling fails you've got bigger problems - body, _ := json.Marshal(notification) - - req := httptest.NewRequest(httpMethod, notificationServiceURI, bytes.NewReader(body)) - - return mux.SetURLVars(req, pathParams) -} - -type mockOutline struct { - methodName string - arg []interface{} - ret []interface{} -} - -func createMockWithOutlines(outlines []mockOutline) interfaces.DBClient { - dbMock := mocks.DBClient{} - - for _, o := range outlines { - dbMock.On(o.methodName, o.arg...).Return(o.ret...) - } - - return &dbMock -} - -func TestDeleteNotificationById(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "OK", - request: createDeleteRequest(map[string]string{ID: TestId}), - dbMock: createMockNotificationDeleter("DeleteNotificationById", TestId, nil), - expectedStatus: http.StatusOK, - }, - { - name: "Unknown Error", - request: createDeleteRequest(map[string]string{ID: TestId}), - dbMock: createMockNotificationDeleter("DeleteNotificationById", TestId, errors.New("Test error")), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Notification not found", - request: createDeleteRequest(map[string]string{ID: TestId}), - dbMock: createMockNotificationDeleter("DeleteNotificationById", TestId, db.ErrNotFound), - expectedStatus: http.StatusNotFound, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restDeleteNotificationByID(rr, tt.request, logger.NewMockClient(), tt.dbMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} -func TestDeleteNotificationBySlug(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "OK", - request: createDeleteRequest(map[string]string{SLUG: TestSlug}), - dbMock: createMockNotificationDeleter("DeleteNotificationBySlug", TestSlug, nil), - expectedStatus: http.StatusOK, - }, - { - name: "Unknown Error", - request: createDeleteRequest(map[string]string{SLUG: TestSlug}), - dbMock: createMockNotificationDeleter("DeleteNotificationBySlug", TestSlug, errors.New("Test error")), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Notification not found", - request: createDeleteRequest(map[string]string{SLUG: TestSlug}), - dbMock: createMockNotificationDeleter("DeleteNotificationBySlug", TestSlug, db.ErrNotFound), - expectedStatus: http.StatusNotFound, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restDeleteNotificationBySlug(rr, tt.request, logger.NewMockClient(), tt.dbMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestDeleteNotificationsByAge(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "OK", - request: createDeleteRequest(map[string]string{AGE: strconv.Itoa(TestAge)}), - dbMock: createMockNotificationAgeDeleter("DeleteNotificationsOld", TestAge, nil), - expectedStatus: http.StatusOK, - }, - { - name: "Unknown Error", - request: createDeleteRequest(map[string]string{AGE: strconv.Itoa(TestAge)}), - dbMock: createMockNotificationAgeDeleter("DeleteNotificationsOld", TestAge, errors.New("Test error")), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Unknown Error", - request: createDeleteRequest(map[string]string{AGE: TestInvalidAge}), - dbMock: createMockNotificationAgeDeleter("DeleteNotificationsOld", TestAge, errors.New("Test error")), - expectedStatus: http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restDeleteNotificationsByAge(rr, tt.request, logger.NewMockClient(), tt.dbMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestGetNotificationsBySender(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "OK", - request: createDeleteRequest(map[string]string{SENDER: TestSender, LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationSenderLoader("GetNotificationBySender", TestSender, TestLimit, nil, createNotifications(1)), - expectedStatus: http.StatusOK, - }, - { - name: "Error converting string to integer", - request: createDeleteRequest(map[string]string{SENDER: TestSender, LIMIT: TestInvalidLimit}), - dbMock: createMockNotificationSenderLoader("GetNotificationBySender", TestSender, TestLimit, errors.New("Test error"), []contract.Notification{}), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Limit too large Error", - request: createDeleteRequest(map[string]string{SENDER: TestSender, LIMIT: strconv.Itoa(TestTooLargeLimit)}), - dbMock: createMockNotificationSenderLoader("GetNotificationBySender", TestSender, TestTooLargeLimit, errors.New("Test error"), []contract.Notification{}), - expectedStatus: http.StatusRequestEntityTooLarge, - }, - { - name: "Not found", - request: createDeleteRequest(map[string]string{SENDER: TestSender, LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationSenderLoader("GetNotificationBySender", TestSender, TestLimit, nil, []contract.Notification{}), - expectedStatus: http.StatusNotFound, - }, - { - name: "Unknown Error", - request: createDeleteRequest(map[string]string{SENDER: TestSender, LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationSenderLoader("GetNotificationBySender", TestSender, TestLimit, errors.New("Test error"), createNotifications(1)), - expectedStatus: http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetNotificationsBySender( - rr, - tt.request, - logger.NewMockClient(), - tt.dbMock, - notificationsConfig.ConfigurationStruct{Service: bootstrapConfig.ServiceInfo{MaxResultCount: 5}}) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestGetNotificationsByStart(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "OK", - request: createRequest(map[string]string{START: strconv.Itoa(int(TestStart)), END: strconv.Itoa(int(TestEnd)), LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationStartLoader("GetNotificationsByStart", TestStart, TestLimit, nil, createNotifications(1)), - expectedStatus: http.StatusOK, - }, - { - name: "Error converting string to integer start", - request: createRequest(map[string]string{START: TestInvalidLimit, END: strconv.Itoa(int(TestEnd)), LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationStartLoader("GetNotificationsByStart", TestStart, TestLimit, errors.New("Test error"), []contract.Notification{}), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Error converting string to integer limit", - request: createRequest(map[string]string{START: strconv.Itoa(int(TestStart)), END: strconv.Itoa(int(TestEnd)), LIMIT: TestInvalidLimit}), - dbMock: createMockNotificationStartLoader("GetNotificationsByStart", TestStart, TestLimit, errors.New("Test error"), []contract.Notification{}), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Limit too large Error", - request: createRequest(map[string]string{START: strconv.Itoa(int(TestStart)), END: strconv.Itoa(int(TestEnd)), LIMIT: strconv.Itoa(TestTooLargeLimit)}), - dbMock: createMockNotificationStartLoader("GetNotificationsByStart", TestStart, TestTooLargeLimit, errors.New("Test error"), []contract.Notification{}), - expectedStatus: http.StatusRequestEntityTooLarge, - }, - { - name: "Not found", - request: createRequest(map[string]string{START: strconv.Itoa(int(TestStart)), END: strconv.Itoa(int(TestEnd)), LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationStartLoader("GetNotificationsByStart", TestStart, TestLimit, nil, []contract.Notification{}), - expectedStatus: http.StatusNotFound, - }, - { - name: "Unknown Error", - request: createRequest(map[string]string{START: strconv.Itoa(int(TestStart)), END: strconv.Itoa(int(TestEnd)), LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationStartLoader("GetNotificationsByStart", TestStart, TestLimit, errors.New("Test error"), createNotifications(1)), - expectedStatus: http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restNotificationByStart( - rr, - tt.request, - logger.NewMockClient(), - tt.dbMock, - notificationsConfig.ConfigurationStruct{Service: bootstrapConfig.ServiceInfo{MaxResultCount: 5}}) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestGetNotificationsByEnd(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "OK", - request: createRequest(map[string]string{START: strconv.Itoa(int(TestStart)), END: strconv.Itoa(int(TestEnd)), LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationStartLoader("GetNotificationsByEnd", TestEnd, TestLimit, nil, createNotifications(1)), - expectedStatus: http.StatusOK, - }, - { - name: "Error converting string to integer end", - request: createRequest(map[string]string{START: strconv.Itoa(int(TestStart)), END: TestInvalidLimit, LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationStartLoader("GetNotificationsByEnd", TestEnd, TestLimit, errors.New("Test error"), []contract.Notification{}), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Error converting string to integer limit", - request: createRequest(map[string]string{START: strconv.Itoa(int(TestStart)), END: strconv.Itoa(int(TestEnd)), LIMIT: TestInvalidLimit}), - dbMock: createMockNotificationStartLoader("GetNotificationsByEnd", TestEnd, TestLimit, errors.New("Test error"), []contract.Notification{}), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Limit too large Error", - request: createRequest(map[string]string{START: strconv.Itoa(int(TestStart)), END: strconv.Itoa(int(TestEnd)), LIMIT: strconv.Itoa(TestTooLargeLimit)}), - dbMock: createMockNotificationStartLoader("GetNotificationsByEnd", TestEnd, TestTooLargeLimit, errors.New("Test error"), []contract.Notification{}), - expectedStatus: http.StatusRequestEntityTooLarge, - }, - { - name: "Not found", - request: createRequest(map[string]string{START: strconv.Itoa(int(TestStart)), END: strconv.Itoa(int(TestEnd)), LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationStartLoader("GetNotificationsByEnd", TestEnd, TestLimit, nil, []contract.Notification{}), - expectedStatus: http.StatusNotFound, - }, - { - name: "Unknown Error", - request: createRequest(map[string]string{START: strconv.Itoa(int(TestStart)), END: strconv.Itoa(int(TestEnd)), LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationStartLoader("GetNotificationsByEnd", TestEnd, TestLimit, errors.New("Test error"), createNotifications(1)), - expectedStatus: http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restNotificationByEnd( - rr, - tt.request, - logger.NewMockClient(), - tt.dbMock, - notificationsConfig.ConfigurationStruct{Service: bootstrapConfig.ServiceInfo{MaxResultCount: 5}}) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestGetNotificationsByStartEnd(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "OK", - request: createRequest(map[string]string{START: strconv.Itoa(int(TestStart)), END: strconv.Itoa(int(TestEnd)), LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationStartEndLoader("GetNotificationsByStartEnd", TestStart, TestEnd, TestLimit, nil, createNotifications(1)), - expectedStatus: http.StatusOK, - }, - { - name: "Error converting string to integer start", - request: createRequest(map[string]string{START: TestInvalidLimit, END: strconv.Itoa(int(TestEnd)), LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationStartEndLoader("GetNotificationsByStartEnd", TestStart, TestEnd, TestLimit, errors.New("Test error"), []contract.Notification{}), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Error converting string to integer end", - request: createRequest(map[string]string{START: strconv.Itoa(int(TestStart)), END: TestInvalidLimit, LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationStartEndLoader("GetNotificationsByStartEnd", TestStart, TestEnd, TestLimit, errors.New("Test error"), []contract.Notification{}), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Error converting string to integer limit", - request: createRequest(map[string]string{START: strconv.Itoa(int(TestStart)), END: strconv.Itoa(int(TestEnd)), LIMIT: TestInvalidLimit}), - dbMock: createMockNotificationStartEndLoader("GetNotificationsByStartEnd", TestStart, TestEnd, TestLimit, errors.New("Test error"), []contract.Notification{}), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Limit too large Error", - request: createRequest(map[string]string{START: strconv.Itoa(int(TestStart)), END: strconv.Itoa(int(TestEnd)), LIMIT: strconv.Itoa(TestTooLargeLimit)}), - dbMock: createMockNotificationStartEndLoader("GetNotificationsByStartEnd", TestStart, TestEnd, TestTooLargeLimit, errors.New("Test error"), []contract.Notification{}), - expectedStatus: http.StatusRequestEntityTooLarge, - }, - { - name: "Not Found", - request: createRequest(map[string]string{START: strconv.Itoa(int(TestStart)), END: strconv.Itoa(int(TestEnd)), LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationStartEndLoader("GetNotificationsByStartEnd", TestStart, TestEnd, TestLimit, nil, []contract.Notification{}), - expectedStatus: http.StatusNotFound, - }, - { - name: "Unknown Error", - request: createRequest(map[string]string{START: strconv.Itoa(int(TestStart)), END: strconv.Itoa(int(TestEnd)), LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationStartEndLoader("GetNotificationsByStartEnd", TestStart, TestEnd, TestLimit, errors.New("Test error"), createNotifications(1)), - expectedStatus: http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restNotificationByStartEnd( - rr, - tt.request, - logger.NewMockClient(), - tt.dbMock, - notificationsConfig.ConfigurationStruct{Service: bootstrapConfig.ServiceInfo{MaxResultCount: 5}}) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestGetNotificationsByLabels(t *testing.T) { - labelsURL := strings.Join(TestLabels, ",") - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "OK", - request: createRequest(map[string]string{LABELS: labelsURL, LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationLabelsLoader("GetNotificationsByLabels", TestLabels, TestLimit, nil, createNotifications(1)), - expectedStatus: http.StatusOK, - }, - { - name: "Error converting string to integer limit", - request: createRequest(map[string]string{LABELS: labelsURL, LIMIT: TestInvalidLimit}), - dbMock: createMockNotificationLabelsLoader("GetNotificationsByLabels", TestLabels, TestLimit, errors.New("Test error"), []contract.Notification{}), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Limit too large Error", - request: createRequest(map[string]string{LABELS: labelsURL, LIMIT: strconv.Itoa(TestTooLargeLimit)}), - dbMock: createMockNotificationLabelsLoader("GetNotificationsByLabels", TestLabels, TestTooLargeLimit, errors.New("Test error"), []contract.Notification{}), - expectedStatus: http.StatusRequestEntityTooLarge, - }, - { - name: "Not Found", - request: createRequest(map[string]string{LABELS: labelsURL, LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationLabelsLoader("GetNotificationsByLabels", TestLabels, TestLimit, nil, []contract.Notification{}), - expectedStatus: http.StatusNotFound, - }, - { - name: "Unknown Error", - request: createRequest(map[string]string{LABELS: labelsURL, LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationLabelsLoader("GetNotificationsByLabels", TestLabels, TestLimit, errors.New("Test error"), createNotifications(1)), - expectedStatus: http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restNotificationsByLabels( - rr, - tt.request, - logger.NewMockClient(), - tt.dbMock, - notificationsConfig.ConfigurationStruct{Service: bootstrapConfig.ServiceInfo{MaxResultCount: 5}}) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestGetNotificationsNewest(t *testing.T) { - labelsURL := strings.Join(TestLabels, ",") - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "OK", - request: createRequest(map[string]string{LABELS: labelsURL, LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationNewestLoader("GetNewNotifications", TestLimit, nil, createNotifications(1)), - expectedStatus: http.StatusOK, - }, - { - name: "Error converting string to integer limit", - request: createRequest(map[string]string{LABELS: labelsURL, LIMIT: TestInvalidLimit}), - dbMock: createMockNotificationNewestLoader("GetNewNotifications", TestLimit, errors.New("Test error"), []contract.Notification{}), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Limit too large Error", - request: createRequest(map[string]string{LABELS: labelsURL, LIMIT: strconv.Itoa(TestTooLargeLimit)}), - dbMock: createMockNotificationNewestLoader("GetNewNotifications", TestTooLargeLimit, errors.New("Test error"), []contract.Notification{}), - expectedStatus: http.StatusRequestEntityTooLarge, - }, - { - name: "Not Found", - request: createRequest(map[string]string{LABELS: labelsURL, LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationNewestLoader("GetNewNotifications", TestLimit, nil, []contract.Notification{}), - expectedStatus: http.StatusNotFound, - }, - { - name: "Unknown Error", - request: createRequest(map[string]string{LABELS: labelsURL, LIMIT: strconv.Itoa(TestLimit)}), - dbMock: createMockNotificationNewestLoader("GetNewNotifications", TestLimit, errors.New("Test error"), createNotifications(1)), - expectedStatus: http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restNotificationsNew( - rr, - tt.request, - logger.NewMockClient(), - tt.dbMock, - notificationsConfig.ConfigurationStruct{Service: bootstrapConfig.ServiceInfo{MaxResultCount: 5}}) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestNotificationHandler(t *testing.T) { - - notificationNormal := createNotificationBySeverityLevel(contract.Normal) - notificationCritical := createNotificationBySeverityLevel(contract.Critical) - notificationInvalid := createInvalidNotification() - notificationBad := createBadNotification() - notificationInvalidCategoriesAndLabels := createInvalidCategoriesAndLabelsNotification() - - var categories []string - categories = append(categories, string(notificationNormal.Category)) - var labels = []string{"first-label", "second-label"} - - var badCategories []string - badCategories = append(badCategories, string(notificationInvalidCategoriesAndLabels.Category)) - var badLabels = []string{"first-bad-label", "second-bad-label"} - - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "ok normal notification", - createNotificationHandlerRequestWithBody(http.MethodPost, notificationNormal, nil), - createMockWithOutlines([]mockOutline{ - {"AddNotification", []interface{}{validateNotification(¬ificationNormal)}, []interface{}{notificationId, nil}}, - {"GetNotificationById", []interface{}{notificationId}, []interface{}{notificationNormal, nil}}, - {"GetSubscriptionByCategoriesLabels", []interface{}{categories, labels}, []interface{}{[]contract.Subscription{}, nil}}, - {"MarkNotificationProcessed", []interface{}{validateNotification(¬ificationNormal)}, []interface{}{nil}}, - }), - http.StatusAccepted, - }, - { - "ok critical notification", - createNotificationHandlerRequestWithBody(http.MethodPost, notificationCritical, nil), - createMockWithOutlines([]mockOutline{ - {"AddNotification", []interface{}{validateNotification(¬ificationCritical)}, []interface{}{notificationId, nil}}, - {"GetNotificationById", []interface{}{notificationId}, []interface{}{notificationCritical, nil}}, - {"GetSubscriptionByCategoriesLabels", []interface{}{categories, labels}, []interface{}{[]contract.Subscription{}, nil}}, - {"MarkNotificationProcessed", []interface{}{validateNotification(¬ificationCritical)}, []interface{}{nil}}, - }), - http.StatusAccepted, - }, - { - "notification validation error", - createNotificationHandlerRequestWithBody(http.MethodPost, notificationInvalid, nil), - createMockWithOutlines([]mockOutline{ - {"AddNotification", []interface{}{validateNotification(¬ificationInvalid)}, []interface{}{notificationId, nil}}, - {"GetNotificationById", []interface{}{invalidNotificationId}, []interface{}{notificationInvalid, nil}}, - {"GetSubscriptionByCategoriesLabels", []interface{}{categories, labels}, []interface{}{[]contract.Subscription{}, nil}}, - {"MarkNotificationProcessed", []interface{}{validateNotification(¬ificationInvalid)}, []interface{}{nil}}, - }), - http.StatusBadRequest, - }, - { - "add notification error", - createNotificationHandlerRequestWithBody(http.MethodPost, notificationCritical, nil), - createMockWithOutlines([]mockOutline{ - {"AddNotification", []interface{}{validateNotification(¬ificationCritical)}, []interface{}{invalidNotificationId, testError}}, - {"GetNotificationById", []interface{}{notificationId}, []interface{}{notificationCritical, nil}}, - {"GetSubscriptionByCategoriesLabels", []interface{}{categories, labels}, []interface{}{[]contract.Subscription{}, nil}}, - {"MarkNotificationProcessed", []interface{}{validateNotification(¬ificationCritical)}, []interface{}{nil}}, - }), - http.StatusConflict, - }, - { - "get notification error", - createNotificationHandlerRequestWithBody(http.MethodPost, notificationCritical, nil), - createMockWithOutlines([]mockOutline{ - {"AddNotification", []interface{}{validateNotification(¬ificationCritical)}, []interface{}{notificationId, nil}}, - {"GetNotificationById", []interface{}{notificationId}, []interface{}{notificationBad, testError}}, - {"GetSubscriptionByCategoriesLabels", []interface{}{categories, labels}, []interface{}{[]contract.Subscription{}, nil}}, - {"MarkNotificationProcessed", []interface{}{validateNotification(¬ificationCritical)}, []interface{}{nil}}, - }), - http.StatusInternalServerError, - }, - { - "distribute and mark notification ok", - createNotificationHandlerRequestWithBody(http.MethodPost, notificationCritical, nil), - createMockWithOutlines([]mockOutline{ - {"AddNotification", []interface{}{validateNotification(¬ificationCritical)}, []interface{}{notificationId, nil}}, - {"GetNotificationById", []interface{}{notificationId}, []interface{}{notificationCritical, nil}}, - {"GetSubscriptionByCategoriesLabels", []interface{}{categories, labels}, []interface{}{[]contract.Subscription{}, nil}}, - {"MarkNotificationProcessed", []interface{}{validateNotification(¬ificationCritical)}, []interface{}{nil}}, - }), - http.StatusAccepted, - }, - { - "distribute and mark notification processed", - createNotificationHandlerRequestWithBody(http.MethodPost, notificationCritical, nil), - createMockWithOutlines([]mockOutline{ - {"AddNotification", []interface{}{validateNotification(¬ificationCritical)}, []interface{}{notificationId, nil}}, - {"GetNotificationById", []interface{}{notificationId}, []interface{}{notificationCritical, nil}}, - {"GetSubscriptionByCategoriesLabels", []interface{}{categories, labels}, []interface{}{[]contract.Subscription{}, testError}}, - {"MarkNotificationProcessed", []interface{}{validateNotification(¬ificationCritical)}, []interface{}{testError}}, - }), - http.StatusOK, - }, - { - "distribute and mark notification error", - createNotificationHandlerRequestWithBody(http.MethodPost, notificationInvalidCategoriesAndLabels, nil), - createMockWithOutlines([]mockOutline{ - {"AddNotification", []interface{}{validateNotification(¬ificationInvalidCategoriesAndLabels)}, []interface{}{notificationId, nil}}, - {"GetNotificationById", []interface{}{notificationId}, []interface{}{notificationInvalidCategoriesAndLabels, nil}}, - {"GetSubscriptionByCategoriesLabels", []interface{}{badCategories, badLabels}, []interface{}{[]contract.Subscription{}, testError}}, - {"MarkNotificationProcessed", []interface{}{validateNotification(¬ificationInvalidCategoriesAndLabels)}, []interface{}{testError}}, - }), - http.StatusBadRequest, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - notificationHandler(rr, - tt.request, - logger.NewMockClient(), - tt.dbMock, - notificationsConfig.ConfigurationStruct{Service: bootstrapConfig.ServiceInfo{MaxResultCount: 5}}) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} diff --git a/internal/support/notifications/rest_subscription.go b/internal/support/notifications/rest_subscription.go deleted file mode 100644 index 5884d5973e..0000000000 --- a/internal/support/notifications/rest_subscription.go +++ /dev/null @@ -1,397 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Technologies Inc. - * - * 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 notifications - -import ( - "encoding/json" - "net/http" - "strings" - - "github.com/edgexfoundry/edgex-go/internal/pkg" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/errors" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/interfaces" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/operators/subscription" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" -) - -func restGetSubscriptions( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - subscriptions, err := dbClient.GetSubscriptions() - if err != nil { - lc.Error(err.Error()) - http.Error(w, err.Error(), http.StatusServiceUnavailable) - return - } - pkg.Encode(subscriptions, w, lc) -} - -func restAddSubscription( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - var s models.Subscription - dec := json.NewDecoder(r.Body) - err := dec.Decode(&s) - - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error("Error decoding subscription: " + err.Error()) - return - } - - // validate email addresses - err = validateEmailAddresses(s) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error(err.Error()) - return - } - - lc.Info("Posting Subscription: " + s.String()) - op := subscription.NewAddExecutor(dbClient, s) - err = op.Execute() - if err != nil { - http.Error(w, err.Error(), http.StatusConflict) - lc.Error(err.Error()) - return - } - - w.WriteHeader(http.StatusCreated) - w.Write([]byte(s.Slug)) -} - -func restUpdateSubscription( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - slug := vars["slug"] - - var s models.Subscription - dec := json.NewDecoder(r.Body) - err := dec.Decode(&s) - - // validate email addresses - err = validateEmailAddresses(s) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error(err.Error()) - return - } - - // Check if the subscription exists - s2, err := dbClient.GetSubscriptionBySlug(s.Slug) - if err != nil { - if err == db.ErrNotFound { - http.Error(w, "Subscription not found", http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } else { - s.ID = s2.ID - } - - lc.Info("Updating subscription by slug: " + slug) - - if err = dbClient.UpdateSubscription(s); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error(err.Error()) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restGetSubscriptionByID( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - id := vars["id"] - op := subscription.NewIdExecutor(dbClient, id) - s, err := op.Execute() - if err != nil { - lc.Error(err.Error()) - switch err.(type) { - case errors.ErrSubscriptionNotFound: - http.Error(w, err.Error(), http.StatusNotFound) - default: - http.Error(w, err.Error(), http.StatusInternalServerError) - } - return - } - pkg.Encode(s, w, lc) -} - -func restDeleteSubscriptionByID( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - id := vars["id"] - lc.Info("Deleting subscription: " + id) - - op := subscription.NewDeleteByIDExecutor(dbClient, id) - err := op.Execute() - if err != nil { - switch err.(type) { - case errors.ErrSubscriptionNotFound: - http.Error(w, err.Error(), http.StatusNotFound) - default: - http.Error(w, err.Error(), http.StatusInternalServerError) - } - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) -} - -func restGetSubscriptionBySlug( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - slug := vars["slug"] - - op := subscription.NewSlugExecutor(dbClient, slug) - s, err := op.Execute() - - if err != nil { - switch err.(type) { - case errors.ErrSubscriptionNotFound: - http.Error(w, err.Error(), http.StatusNotFound) - default: - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - pkg.Encode(s, w, lc) -} - -func restDeleteSubscriptionBySlug( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - slug := vars["slug"] - - lc.Info("Deleting subscription by slug: " + slug) - - op := subscription.NewDeleteBySlugExecutor(dbClient, slug) - err := op.Execute() - if err != nil { - switch err.(type) { - case errors.ErrSubscriptionNotFound: - http.Error(w, err.Error(), http.StatusNotFound) - default: - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restGetSubscriptionsByCategories( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - categories := splitVars(vars["categories"]) - op := subscription.NewCategoriesExecutor(dbClient, categories) - s, err := op.Execute() - if err != nil { - if err == db.ErrNotFound { - http.Error(w, "Subscription not found", http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - pkg.Encode(s, w, lc) -} - -func subscriptionsByLabelsHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - - labels := splitVars(vars["labels"]) - - s, err := dbClient.GetSubscriptionByLabels(labels) - if err != nil { - if err == db.ErrNotFound { - http.Error(w, "Subscription not found", http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - pkg.Encode(s, w, lc) - -} - -func subscriptionsByCategoriesLabelsHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - - labels := splitVars(vars["labels"]) - categories := splitVars(vars["categories"]) - - s, err := dbClient.GetSubscriptionByCategoriesLabels(categories, labels) - if err != nil { - if err == db.ErrNotFound { - http.Error(w, "Subscription not found", http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - pkg.Encode(s, w, lc) - -} - -func splitVars(vars string) []string { - return strings.Split(vars, ",") -} - -func validateEmailAddresses(s models.Subscription) error { - var invalidAddrs []string - for _, c := range s.Channels { - if c.Type == models.ChannelType(models.Email) { - for _, m := range c.MailAddresses { - if strings.ContainsAny(m, "\n\r") { - invalidAddrs = append(invalidAddrs, m) - } - } - } - } - if len(invalidAddrs) > 0 { - resp := "Addresses contain invalid CRLF characters" - return errors.NewErrInvalidEmailAddresses(invalidAddrs, resp) - } - return nil -} - -func subscriptionsByReceiverHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - - s, err := dbClient.GetSubscriptionByReceiver(vars["receiver"]) - if err != nil { - if err == db.ErrNotFound { - http.Error(w, "Subscription not found", http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - pkg.Encode(s, w, lc) - -} diff --git a/internal/support/notifications/rest_subscription_test.go b/internal/support/notifications/rest_subscription_test.go deleted file mode 100644 index 29fe36467f..0000000000 --- a/internal/support/notifications/rest_subscription_test.go +++ /dev/null @@ -1,567 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Technologies Inc. - * - * 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 notifications - -import ( - "bytes" - "encoding/json" - "errors" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/interfaces" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/interfaces/mocks" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" -) - -var TestSubscriptionURI = "/subscription" - -var subscriptionForAdd = contract.Subscription{ - - Slug: "notice-test-123", - Receiver: "System Admin", - SubscribedCategories: []contract.NotificationsCategory{ - "SECURITY", - "HW_HEALTH", - "SW_HEALTH", - }, - SubscribedLabels: []string{ - "Dell", - "IoT", - "test", - }, - Channels: []contract.Channel{ - { - Type: "REST", - Url: "http://abc.def/alert", - }, - { - Type: "EMAIL", - MailAddresses: []string{ - "cloud@abc.def", - "jack@abc.def", - }, - }, - }, -} - -var subscriptionForAddInvalid = contract.Subscription{ - - Slug: "notice-test-123", - Receiver: "System Admin", - SubscribedCategories: []contract.NotificationsCategory{ - "SECURITY", - "HW_HEALTH", - "SW_HEALTH", - }, - SubscribedLabels: []string{ - "Dell", - "IoT", - "test", - }, - Channels: []contract.Channel{ - { - Type: "REST", - Url: "http://abc.def/alert", - }, - { - Type: "EMAIL", - MailAddresses: []string{ - "cloud\n", - "jack\r", - }, - }, - }, -} - -func TestSubscriptionsAll(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "OK", - request: createSubscriptionRequest(map[string]string{}), - dbMock: createMockSubscriptionAllLoader("GetSubscriptions", nil), - expectedStatus: http.StatusOK, - }, - - { - name: "Other error from database", - request: createSubscriptionRequest(map[string]string{}), - dbMock: createMockSubscriptionAllLoader("GetSubscriptions", errors.New("test error")), - expectedStatus: http.StatusServiceUnavailable, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetSubscriptions(rr, tt.request, logger.NewMockClient(), tt.dbMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestGetSubscriptionById(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "Subscription not found", - request: createSubscriptionRequest(map[string]string{ID: TestId}), - dbMock: createMockSubscriptionLoader("GetSubscriptionById", TestId, db.ErrNotFound), - expectedStatus: http.StatusNotFound, - }, - { - name: "OK", - request: createSubscriptionRequest(map[string]string{ID: TestId}), - dbMock: createMockSubscriptionLoader("GetSubscriptionById", TestId, nil), - expectedStatus: http.StatusOK, - }, - - { - name: "Other error from database", - request: createSubscriptionRequest(map[string]string{ID: TestId}), - dbMock: createMockSubscriptionLoader("GetSubscriptionById", TestId, errors.New("Test error")), - expectedStatus: http.StatusInternalServerError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetSubscriptionByID(rr, tt.request, logger.NewMockClient(), tt.dbMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func createSubscriptionRequest(params map[string]string) *http.Request { - req := httptest.NewRequest(http.MethodGet, TestSubscriptionURI, nil) - return mux.SetURLVars(req, params) -} - -func createSubscriptionDeleteRequest(params map[string]string) *http.Request { - req := httptest.NewRequest(http.MethodDelete, TestURI, nil) - return mux.SetURLVars(req, params) -} - -func createMockSubscriptionAllLoader(methodName string, desiredError error) interfaces.DBClient { - myMock := mocks.DBClient{} - - if desiredError != nil { - myMock.On(methodName).Return([]contract.Subscription{}, desiredError) - } else { - myMock.On(methodName).Return(createSubscriptions(1), nil) - } - return &myMock -} - -func createMockSubscriptionLoader(methodName string, testID string, desiredError error) interfaces.DBClient { - myMock := mocks.DBClient{} - - if desiredError != nil { - myMock.On(methodName, testID).Return(contract.Subscription{}, desiredError) - } else { - myMock.On(methodName, testID).Return(createSubscriptions(1)[0], nil) - } - return &myMock -} - -func createMockSubscriptionDeleter(methodName string, testID string, desiredError error) interfaces.DBClient { - myMock := mocks.DBClient{} - - if desiredError != nil { - myMock.On(methodName, testID).Return(desiredError) - } else { - myMock.On(methodName, testID).Return(nil) - } - return &myMock -} - -func createMockSubscriptionLoaderCollection(methodName string, arg interface{}, desiredError error) interfaces.DBClient { - myMock := mocks.DBClient{} - - if desiredError != nil { - myMock.On(methodName, arg).Return([]contract.Subscription{}, desiredError) - } else { - myMock.On(methodName, arg).Return(createSubscriptions(1), nil) - } - return &myMock -} - -func createSubscriptions(howMany int) []contract.Subscription { - var notifications []contract.Subscription - for i := 0; i < howMany; i++ { - notifications = append(notifications, contract.Subscription{ - Slug: "notice-test-123", - Receiver: "System Admin", - SubscribedCategories: []contract.NotificationsCategory{ - "SECURITY", - "HW_HEALTH", - "SW_HEALTH", - }, - SubscribedLabels: []string{ - "Dell", - "IoT", - "test", - }, - Channels: []contract.Channel{ - { - Type: "REST", - Url: "http://abc.def/alert", - }, - { - Type: "EMAIL", - MailAddresses: []string{ - "cloud@abc.def", - "jack@abc.def", - }, - }, - }, - }) - } - return notifications -} - -func TestDeleteSubscriptionById(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "OK", - request: createSubscriptionDeleteRequest(map[string]string{ID: TestId}), - dbMock: createMockSubscriptionDeleter("DeleteSubscriptionById", TestId, nil), - expectedStatus: http.StatusOK, - }, - { - name: "Unknown Error", - request: createSubscriptionDeleteRequest(map[string]string{ID: TestId}), - dbMock: createMockSubscriptionDeleter("DeleteSubscriptionById", TestId, errors.New("Test error")), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Subscription not found", - request: createSubscriptionDeleteRequest(map[string]string{ID: TestId}), - dbMock: createMockSubscriptionDeleter("DeleteSubscriptionById", TestId, db.ErrNotFound), - expectedStatus: http.StatusNotFound, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restDeleteSubscriptionByID(rr, tt.request, logger.NewMockClient(), tt.dbMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestGetSubscriptionBySlug(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "Subscription not found", - request: createSubscriptionRequest(map[string]string{SLUG: TestSlug}), - dbMock: createMockSubscriptionLoader("GetSubscriptionBySlug", TestSlug, db.ErrNotFound), - expectedStatus: http.StatusNotFound, - }, - { - name: "OK", - request: createSubscriptionRequest(map[string]string{SLUG: TestSlug}), - dbMock: createMockSubscriptionLoader("GetSubscriptionBySlug", TestSlug, nil), - expectedStatus: http.StatusOK, - }, - - { - name: "Other error from database", - request: createSubscriptionRequest(map[string]string{SLUG: TestSlug}), - dbMock: createMockSubscriptionLoader("GetSubscriptionBySlug", TestSlug, errors.New("Test error")), - expectedStatus: http.StatusInternalServerError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetSubscriptionBySlug(rr, tt.request, logger.NewMockClient(), tt.dbMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestDeleteSubscriptionBySlug(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "OK", - request: createSubscriptionDeleteRequest(map[string]string{SLUG: TestSlug}), - dbMock: createMockSubscriptionDeleter("DeleteSubscriptionBySlug", TestSlug, nil), - expectedStatus: http.StatusOK, - }, - { - name: "Unknown Error", - request: createSubscriptionDeleteRequest(map[string]string{SLUG: TestSlug}), - dbMock: createMockSubscriptionDeleter("DeleteSubscriptionBySlug", TestSlug, errors.New("Test error")), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Subscription not found", - request: createSubscriptionDeleteRequest(map[string]string{SLUG: TestSlug}), - dbMock: createMockSubscriptionDeleter("DeleteSubscriptionBySlug", TestSlug, db.ErrNotFound), - expectedStatus: http.StatusNotFound, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restDeleteSubscriptionBySlug(rr, tt.request, logger.NewMockClient(), tt.dbMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestGetSubscriptionsByCategories(t *testing.T) { - - categoriesURL := strings.Join(TestCategories, ",") - - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "Subscription not found", - request: createSubscriptionRequest(map[string]string{CATEGORIES: categoriesURL}), - dbMock: createMockSubscriptionLoaderCollection("GetSubscriptionByCategories", TestCategories, db.ErrNotFound), - expectedStatus: http.StatusNotFound, - }, - { - name: "OK", - request: createSubscriptionRequest(map[string]string{CATEGORIES: categoriesURL}), - dbMock: createMockSubscriptionLoaderCollection("GetSubscriptionByCategories", TestCategories, nil), - expectedStatus: http.StatusOK, - }, - - { - name: "Other error from database", - request: createSubscriptionRequest(map[string]string{CATEGORIES: categoriesURL}), - dbMock: createMockSubscriptionLoaderCollection("GetSubscriptionByCategories", TestCategories, errors.New("Test error")), - expectedStatus: http.StatusInternalServerError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetSubscriptionsByCategories(rr, tt.request, logger.NewMockClient(), tt.dbMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestAddSubscription(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "OK", - request: createRequestSubscriptionAdd(subscriptionForAdd), - dbMock: createMockSubscriptionLoaderAddSuccess(), - expectedStatus: http.StatusCreated, - }, - { - name: "Invalid Email", - request: createRequestSubscriptionAdd(subscriptionForAddInvalid), - dbMock: createMockSubscriptionLoaderAddSuccess(), - expectedStatus: http.StatusBadRequest, - }, - { - name: "Unexpected Error", - request: createRequestSubscriptionAdd(subscriptionForAdd), - dbMock: createMockSubscriptionLoaderAddErr(), - expectedStatus: http.StatusConflict, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restAddSubscription(rr, tt.request, logger.NewMockClient(), tt.dbMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func createRequestSubscriptionAdd(subscription contract.Subscription) *http.Request { - b, _ := json.Marshal(subscription) - req := httptest.NewRequest(http.MethodPost, TestURI, bytes.NewBuffer(b)) - return mux.SetURLVars(req, map[string]string{}) -} - -func createMockSubscriptionLoaderAddSuccess() interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On("AddSubscription", subscriptionForAdd).Return(subscriptionForAdd.ID, nil) - return &myMock -} - -func createMockSubscriptionLoaderAddErr() interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On("AddSubscription", subscriptionForAdd).Return(subscriptionForAdd.ID, errors.New("test error")) - return &myMock -} - -func TestUpdateSubscription(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "OK", - request: createRequestSubscriptionUpdate(subscriptionForAdd), - dbMock: createMockSubscriptionLoaderUpdateSuccess(), - expectedStatus: http.StatusOK, - }, - { - name: "Invalid Email", - request: createRequestSubscriptionUpdate(subscriptionForAddInvalid), - dbMock: createMockSubscriptionLoaderUpdateSuccess(), - expectedStatus: http.StatusBadRequest, - }, - { - name: "GetSubscriptionBySlug Not Found Error", - request: createRequestSubscriptionUpdate(subscriptionForAdd), - dbMock: createMockSubscriptionLoaderUpdateGetNotFoundErr(), - expectedStatus: http.StatusNotFound, - }, - { - name: "GetSubscriptionBySlug Unexpected Error", - request: createRequestSubscriptionUpdate(subscriptionForAdd), - dbMock: createMockSubscriptionLoaderUpdateGetErr(), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "Unexpected Error", - request: createRequestSubscriptionUpdate(subscriptionForAdd), - dbMock: createMockSubscriptionLoaderUpdateErr(), - expectedStatus: http.StatusInternalServerError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restUpdateSubscription(rr, tt.request, logger.NewMockClient(), tt.dbMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func createRequestSubscriptionUpdate(subscription contract.Subscription) *http.Request { - b, _ := json.Marshal(subscription) - req := httptest.NewRequest(http.MethodPut, TestURI, bytes.NewBuffer(b)) - return mux.SetURLVars(req, map[string]string{}) -} - -func createMockSubscriptionLoaderUpdateSuccess() interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On("GetSubscriptionBySlug", subscriptionForAdd.Slug).Return(subscriptionForAdd, nil) - myMock.On("UpdateSubscription", subscriptionForAdd).Return(nil) - return &myMock -} - -func createMockSubscriptionLoaderUpdateErr() interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On("GetSubscriptionBySlug", subscriptionForAdd.Slug).Return(subscriptionForAdd, nil) - myMock.On("UpdateSubscription", subscriptionForAdd).Return(errors.New("test error")) - return &myMock -} - -func createMockSubscriptionLoaderUpdateGetNotFoundErr() interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On("GetSubscriptionBySlug", subscriptionForAdd.Slug).Return(subscriptionForAdd, db.ErrNotFound) - myMock.On("UpdateSubscription", subscriptionForAdd).Return(errors.New("test error")) - return &myMock -} - -func createMockSubscriptionLoaderUpdateGetErr() interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On("GetSubscriptionBySlug", subscriptionForAdd.Slug).Return(subscriptionForAdd, errors.New("test error")) - myMock.On("UpdateSubscription", subscriptionForAdd).Return(errors.New("test error")) - return &myMock -} diff --git a/internal/support/notifications/rest_transmission.go b/internal/support/notifications/rest_transmission.go deleted file mode 100644 index f4c7354c6b..0000000000 --- a/internal/support/notifications/rest_transmission.go +++ /dev/null @@ -1,394 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Technologies Inc. - * - * 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 notifications - -import ( - "encoding/json" - "fmt" - "net/http" - "strconv" - - "github.com/edgexfoundry/edgex-go/internal/pkg" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/interfaces" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" -) - -func transmissionHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - var t models.Transmission - dec := json.NewDecoder(r.Body) - err := dec.Decode(&t) - - // Problem Decoding Transmission - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error("Error decoding transmission: " + err.Error()) - return - } - - lc.Info("Posting Transmission: " + t.String()) - id, err := dbClient.AddTransmission(t) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error(err.Error()) - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte(id)) - -} - -func transmissionBySlugHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - limitNum, err := strconv.Atoi(vars["limit"]) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting limit to integer: " + err.Error()) - return - } - - t, err := dbClient.GetTransmissionsByNotificationSlug(vars["slug"], limitNum) - if err != nil { - if err == db.ErrNotFound { - http.Error(w, "Transmission not found", http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - pkg.Encode(t, w, lc) - -} - -func transmissionBySlugAndStartEndHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - slug := vars["slug"] - start, err := strconv.ParseInt(vars["start"], 10, 64) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error(fmt.Sprintf("failed to parse start %s %s", vars["start"], err.Error())) - return - } - end, err := strconv.ParseInt(vars["end"], 10, 64) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error(fmt.Sprintf("failed to parse end %s %s", vars["end"], err.Error())) - return - } - limitNum, err := strconv.Atoi(vars["limit"]) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error(fmt.Sprintf("failed to parse limit %s %s", vars["limit"], err.Error())) - return - } - - t, err := dbClient.GetTransmissionsByNotificationSlugAndStartEnd(slug, start, end, limitNum) - if err != nil { - if err == db.ErrNotFound { - http.Error(w, "Transmission not found", http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - pkg.Encode(t, w, lc) - -} - -func transmissionByStartEndHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - start, err := strconv.ParseInt(vars["start"], 10, 64) - // Problem converting start - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting the start to an integer") - return - } - end, err := strconv.ParseInt(vars["end"], 10, 64) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting the end to an integer") - return - } - limitNum, err := strconv.Atoi(vars["limit"]) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting limit to integer: " + err.Error()) - return - } - - t, err := dbClient.GetTransmissionsByStartEnd(start, end, limitNum) - if err != nil { - if err == db.ErrNotFound { - http.Error(w, "Transmission not found", http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - pkg.Encode(t, w, lc) - -} - -func transmissionByStartHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - vars := mux.Vars(r) - start, err := strconv.ParseInt(vars["start"], 10, 64) - // Problem converting start - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting the start to an integer") - return - } - limitNum, err := strconv.Atoi(vars["limit"]) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting limit to integer: " + err.Error()) - return - } - - t, err := dbClient.GetTransmissionsByStart(start, limitNum) - if err != nil { - if err == db.ErrNotFound { - http.Error(w, "Transmission not found", http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - pkg.Encode(t, w, lc) - -} - -func transmissionByEndHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - end, err := strconv.ParseInt(vars["end"], 10, 64) - // Problem converting start - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting the end to an integer") - return - } - limitNum, err := strconv.Atoi(vars["limit"]) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting limit to integer: " + err.Error()) - return - } - - t, err := dbClient.GetTransmissionsByEnd(end, limitNum) - if err != nil { - if err == db.ErrNotFound { - http.Error(w, "Transmission not found", http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - pkg.Encode(t, w, lc) - -} - -func transmissionByEscalatedHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - transmissionByStatusHandler(w, r, models.Trxescalated, lc, dbClient) -} - -func transmissionByFailedHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - transmissionByStatusHandler(w, r, models.Failed, lc, dbClient) -} - -func transmissionByStatusHandler( - w http.ResponseWriter, - r *http.Request, - status models.TransmissionStatus, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - limitNum, err := strconv.Atoi(vars["limit"]) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting limit to integer: " + err.Error()) - return - } - - switch r.Method { - case http.MethodGet: - - t, err := dbClient.GetTransmissionsByStatus(limitNum, status) - if err != nil { - if err == db.ErrNotFound { - http.Error(w, "Transmission not found", http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - pkg.Encode(t, w, lc) - } -} - -func transmissionByAgeSentHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - transmissionByAgeStatusHandler(w, r, models.Sent, lc, dbClient) -} - -func transmissionByAgeEscalatedHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - transmissionByAgeStatusHandler(w, r, models.Trxescalated, lc, dbClient) -} - -func transmissionByAgeAcknowledgedHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - transmissionByAgeStatusHandler(w, r, models.Acknowledged, lc, dbClient) -} - -func transmissionByAgeFailedHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - transmissionByAgeStatusHandler(w, r, models.Failed, lc, dbClient) -} - -func transmissionByAgeStatusHandler( - w http.ResponseWriter, - r *http.Request, - status models.TransmissionStatus, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - vars := mux.Vars(r) - age, err := strconv.ParseInt(vars["age"], 10, 64) - // Problem converting age - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error("Error converting the age to an integer") - return - } - - err = dbClient.DeleteTransmission(age, status) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - lc.Error(err.Error()) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) - -} diff --git a/internal/support/notifications/router.go b/internal/support/notifications/router.go deleted file mode 100644 index f0faf4890c..0000000000 --- a/internal/support/notifications/router.go +++ /dev/null @@ -1,413 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Technologies Inc. - * - * 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 notifications - -import ( - "net/http" - - "github.com/edgexfoundry/edgex-go/internal/pkg" - "github.com/edgexfoundry/edgex-go/internal/pkg/bootstrap/container" - "github.com/edgexfoundry/edgex-go/internal/pkg/correlation" - "github.com/edgexfoundry/edgex-go/internal/pkg/telemetry" - notificationsContainer "github.com/edgexfoundry/edgex-go/internal/support/notifications/container" - - bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/container" - "github.com/edgexfoundry/go-mod-bootstrap/v2/di" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - - "github.com/gorilla/mux" -) - -func loadRestRoutes(r *mux.Router, dic *di.Container) { - // Ping Resource - r.HandleFunc( - clients.ApiPingRoute, - func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set(clients.ContentType, clients.ContentTypeText) - w.Write([]byte("pong")) - }).Methods(http.MethodGet) - - // Configuration - r.HandleFunc( - clients.ApiConfigRoute, - func(w http.ResponseWriter, _ *http.Request) { - pkg.Encode(*notificationsContainer.ConfigurationFrom(dic.Get), w, bootstrapContainer.LoggingClientFrom(dic.Get)) - }).Methods(http.MethodGet) - - // Metrics - r.HandleFunc( - clients.ApiMetricsRoute, - func(w http.ResponseWriter, _ *http.Request) { - pkg.Encode(telemetry.NewSystemUsage(), w, bootstrapContainer.LoggingClientFrom(dic.Get)) - }).Methods(http.MethodGet) - - // Version - r.HandleFunc(clients.ApiVersionRoute, pkg.VersionHandler).Methods(http.MethodGet) - - b := r.PathPrefix(clients.ApiBase).Subrouter() - - // Notifications - b.HandleFunc( - "/"+NOTIFICATION, - func(w http.ResponseWriter, r *http.Request) { - notificationHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - *notificationsContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPost) - b.HandleFunc( - "/"+NOTIFICATION+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetNotificationByID( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+NOTIFICATION+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteNotificationByID( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodDelete) - b.HandleFunc( - "/"+NOTIFICATION+"/"+SLUG+"/{"+SLUG+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetNotificationBySlug( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+NOTIFICATION+"/"+SLUG+"/{"+SLUG+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteNotificationBySlug( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodDelete) - b.HandleFunc( - "/"+NOTIFICATION+"/"+AGE+"/{"+AGE+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteNotificationsByAge( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodDelete) - b.HandleFunc( - "/"+NOTIFICATION+"/"+SENDER+"/{"+SENDER+"}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - restGetNotificationsBySender( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - *notificationsContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+NOTIFICATION+"/"+START+"/{"+START+"}/"+END+"/{"+END+"}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - restNotificationByStartEnd( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - *notificationsContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+NOTIFICATION+"/"+START+"/{"+START+"}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - restNotificationByStart( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - *notificationsContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+NOTIFICATION+"/"+END+"/{"+END+"}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - restNotificationByEnd( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - *notificationsContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+NOTIFICATION+"/"+LABELS+"/{"+LABELS+"}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - restNotificationsByLabels( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - *notificationsContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+NOTIFICATION+"/"+NEW+"/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - restNotificationsNew( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - *notificationsContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - - // GetSubscriptions - b.HandleFunc( - "/"+SUBSCRIPTION, - func(w http.ResponseWriter, r *http.Request) { - restGetSubscriptions( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+SUBSCRIPTION, - func(w http.ResponseWriter, r *http.Request) { - restAddSubscription( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodPost) - b.HandleFunc( - "/"+SUBSCRIPTION, - func(w http.ResponseWriter, r *http.Request) { - restUpdateSubscription( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodPut) - b.HandleFunc( - "/"+SUBSCRIPTION+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetSubscriptionByID( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+SUBSCRIPTION+"/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteSubscriptionByID( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodDelete) - b.HandleFunc( - "/"+SUBSCRIPTION+"/"+SLUG+"/{"+SLUG+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetSubscriptionBySlug( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+SUBSCRIPTION+"/"+SLUG+"/{"+SLUG+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteSubscriptionBySlug( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodDelete) - b.HandleFunc( - "/"+SUBSCRIPTION+"/"+CATEGORIES+"/{"+CATEGORIES+"}/"+LABELS+"/{"+LABELS+"}", - func(w http.ResponseWriter, r *http.Request) { - subscriptionsByCategoriesLabelsHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+SUBSCRIPTION+"/"+CATEGORIES+"/{"+CATEGORIES+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetSubscriptionsByCategories( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+SUBSCRIPTION+"/"+LABELS+"/{"+LABELS+"}", - func(w http.ResponseWriter, r *http.Request) { - subscriptionsByLabelsHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+SUBSCRIPTION+"/"+RECEIVER+"/{"+RECEIVER+"}", - func(w http.ResponseWriter, r *http.Request) { - subscriptionsByReceiverHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - - // Transmissions - b.HandleFunc( - "/"+TRANSMISSION, - func(w http.ResponseWriter, r *http.Request) { - transmissionHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodPost) - b.HandleFunc( - "/"+TRANSMISSION+"/"+SLUG+"/{"+SLUG+"}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - transmissionBySlugHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+TRANSMISSION+"/"+SLUG+"/{"+SLUG+"}/"+START+"/{"+START+"}/"+END+"/{"+END+"}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - transmissionBySlugAndStartEndHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+TRANSMISSION+"/"+START+"/{"+START+"}/"+END+"/{"+END+"}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - transmissionByStartEndHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+TRANSMISSION+"/"+START+"/{"+START+"}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - transmissionByStartHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+TRANSMISSION+"/"+END+"/{"+END+"}/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - transmissionByEndHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+TRANSMISSION+"/"+ESCALATED+"/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - transmissionByEscalatedHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+TRANSMISSION+"/"+FAILED+"/{"+LIMIT+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - transmissionByFailedHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - b.HandleFunc( - "/"+TRANSMISSION+"/"+SENT+"/"+AGE+"/{"+AGE+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - transmissionByAgeSentHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodDelete) - b.HandleFunc( - "/"+TRANSMISSION+"/"+ESCALATED+"/"+AGE+"/{"+AGE+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - transmissionByAgeEscalatedHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodDelete) - b.HandleFunc( - "/"+TRANSMISSION+"/"+ACKNOWLEDGED+"/"+AGE+"/{"+AGE+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - transmissionByAgeAcknowledgedHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodDelete) - b.HandleFunc( - "/"+TRANSMISSION+"/"+FAILED+"/"+AGE+"/{"+AGE+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - transmissionByAgeFailedHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodDelete) - - // Cleanup - b.HandleFunc( - "/"+CLEANUP, - func(w http.ResponseWriter, r *http.Request) { - cleanupHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodDelete) - b.HandleFunc( - "/"+CLEANUP+"/"+AGE+"/{"+AGE+":[0-9]+}", - func(w http.ResponseWriter, r *http.Request) { - cleanupAgeHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodDelete) - - r.Use(correlation.ManageHeader) - r.Use(correlation.LoggingMiddleware(bootstrapContainer.LoggingClientFrom(dic.Get))) -} diff --git a/internal/support/notifications/sending_service.go b/internal/support/notifications/sending_service.go deleted file mode 100644 index a5d69105f4..0000000000 --- a/internal/support/notifications/sending_service.go +++ /dev/null @@ -1,294 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Technologies Inc. - * - * 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 notifications - -import ( - "bytes" - "crypto/tls" - "errors" - "fmt" - "net" - "net/http" - mail "net/smtp" - "strconv" - "strings" - "time" - - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - notificationsConfig "github.com/edgexfoundry/edgex-go/internal/support/notifications/config" - "github.com/edgexfoundry/edgex-go/internal/support/notifications/interfaces" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -func sendViaChannel( - n models.Notification, - c models.Channel, - receiver string, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - config notificationsConfig.ConfigurationStruct) { - - lc.Debug("Sending notification: " + n.Slug + ", via channel: " + c.String()) - var tr models.TransmissionRecord - if c.Type == models.ChannelType(models.Email) { - tr = sendMail(n.Content, c.MailAddresses, n.ContentType, lc, config.Smtp) - } else { - tr = restSend(n.Content, c.Url, n.ContentType, lc) - } - t, err := persistTransmission(tr, n, c, receiver, lc, dbClient) - if err == nil { - handleFailedTransmission(t, lc, dbClient, config) - } -} - -func resendViaChannel( - t models.Transmission, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - config notificationsConfig.ConfigurationStruct) { - - var tr models.TransmissionRecord - if t.Channel.Type == models.ChannelType(models.Email) { - tr = sendMail(t.Notification.Content, t.Channel.MailAddresses, t.Notification.ContentType, lc, config.Smtp) - } else { - tr = restSend(t.Notification.Content, t.Channel.Url, t.Notification.ContentType, lc) - } - t.ResendCount = t.ResendCount + 1 - t.Status = tr.Status - t.Records = append(t.Records, tr) - err := dbClient.UpdateTransmission(t) - if err == nil { - handleFailedTransmission(t, lc, dbClient, config) - } -} - -func getTransmissionRecord(msg string, st models.TransmissionStatus) models.TransmissionRecord { - tr := models.TransmissionRecord{} - tr.Sent = db.MakeTimestamp() - tr.Status = st - tr.Response = msg - return tr -} - -func persistTransmission( - tr models.TransmissionRecord, - n models.Notification, - c models.Channel, - rec string, - lc logger.LoggingClient, - dbClient interfaces.DBClient) (models.Transmission, error) { - - trx := models.Transmission{Notification: n, Receiver: rec, Channel: c, ResendCount: 0, Status: tr.Status} - trx.Records = []models.TransmissionRecord{tr} - id, err := dbClient.AddTransmission(trx) - if err != nil { - lc.Error("Transmission cannot be persisted: " + trx.String()) - return trx, err - } - - //We need to fetch this transmission for later use in retries, otherwise timestamp information will be lost. - trx, err = dbClient.GetTransmissionById(id) - if err != nil { - lc.Error("error fetching newly saved transmission: " + id) - return models.Transmission{}, err - } - return trx, nil -} - -func sendMail( - message string, - addressees []string, - contentType string, - lc logger.LoggingClient, - smtp notificationsConfig.SmtpInfo) models.TransmissionRecord { - - tr := getTransmissionRecord("SMTP server received", models.Sent) - - smtpMessage := buildSmtpMessage(smtp.Sender, smtp.Subject, addressees, contentType, message) - - err := smtpSend(addressees, smtpMessage, smtp) - if err != nil { - lc.Error("Problems sending message to: " + strings.Join(addressees, ",") + ", issue: " + err.Error()) - tr.Status = models.Failed - tr.Response = err.Error() - return tr - } - return tr -} - -func buildSmtpMessage(sender string, subject string, toAddresses []string, contentType string, message string) []byte { - smtpNewline := "\r\n" - - // required CRLF at ends of lines and CRLF between header and body for SMTP RFC 822 style email - buf := bytes.NewBufferString("Subject: " + subject + smtpNewline) - - buf.WriteString("From: " + sender + smtpNewline) - - buf.WriteString("To: " + strings.Join(toAddresses, ",") + smtpNewline) - - // only add MIME header if notification content type was set - // maybe provide charset overrides as well? - if contentType != "" { - buf.WriteString(fmt.Sprintf("MIME-version: 1.0;\r\nContent-Type: %s; charset=\"UTF-8\";\r\n", contentType)) - } - - buf.WriteString(smtpNewline) - - //maximum line size is 1000 - //split on newline first then break further as needed - for _, line := range strings.Split(message, smtpNewline) { - ln := 998 - idx := 0 - for len(line) > idx+ln { - buf.WriteString(line[idx:idx+ln] + smtpNewline) - idx += ln - } - buf.WriteString(line[idx:] + smtpNewline) - } - - return []byte(buf.String()) -} - -func restSend(message string, url string, contentType string, lc logger.LoggingClient) models.TransmissionRecord { - tr := getTransmissionRecord("", models.Sent) - - if contentType == "" { - contentType = "text/plain" - } - - rs, err := http.Post(url, contentType, bytes.NewBuffer([]byte(message))) - if err != nil { - lc.Error("Problems sending message to: " + url) - lc.Error("Error indication was: " + err.Error()) - tr.Status = models.Failed - tr.Response = err.Error() - return tr - } - tr.Response = "Got response status code: " + rs.Status - return tr -} - -func handleFailedTransmission( - t models.Transmission, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - config notificationsConfig.ConfigurationStruct) { - - n := t.Notification - if t.ResendCount >= config.Writable.ResendLimit { - lc.Error("Too many transmission resend attempts! Giving up on transmission: " + t.ID + ", for notification: " + n.Slug) - } - if t.Status == models.Failed && n.Status != models.Escalated { - lc.Debug("Handling failed transmission for: " + t.ID + " for notification: " + t.Notification.Slug + ", resends so far: " + strconv.Itoa(t.ResendCount)) - if n.Severity == models.Critical { - if t.ResendCount < config.Writable.ResendLimit { - time.AfterFunc(time.Second*5, func() { - criticalSeverityResend(t, lc, dbClient, config) - }) - } else { - escalate(t, lc, dbClient, config) - t.Status = models.Trxescalated - dbClient.UpdateTransmission(t) - } - } - } -} - -func deduceAuth(s notificationsConfig.SmtpInfo) (mail.Auth, error) { - if s.CheckUsername() == "" && s.Password == "" { - return nil, errors.New("Notifications: Expecting username") - } - if s.CheckUsername() != "" && s.Password == "" { - return nil, nil - } - if s.CheckUsername() == "" && s.Password != "" { - return nil, errors.New("Notifications: Expecting username") - } - return mail.PlainAuth("", s.CheckUsername(), s.Password, s.Host), nil -} - -// The function smtpSend replicates the functionality provided by the SendMail function -// from smtp package. A rivision of standard function was needed because smtp.SendMail -// does not allow for set-reset of InsecureSkipVerify flag of tls.Config structure. This -// flag is needed to be manipulated for allowing the self-signed certificates. -// -// As it is replicating the functionality from smtp.SendMail, it borrows heavily from the -// original function in its design and implementation. This version adds new functionality -// for handling the SmtpInfo configuration and authentication management, along with the -// requirement of ability to set-reset the InsecureSkipVerify flag. -// -// This is using a lot of unexported methods and types from smtp package through exported -// interfaces, which makes it a little bit trickier to modify. Since, the intention for -// this function is to use it as a support function for handling the low level SMTP -// protocol mechanism, it is not exported. -func smtpSend(to []string, msg []byte, s notificationsConfig.SmtpInfo) error { - addr := s.Host + ":" + strconv.Itoa(s.Port) - auth, err := deduceAuth(s) - if err != nil { - return err - } - c, err := mail.Dial(addr) - if err != nil { - return errors.New("Notifications: Error dialing address") - } - defer c.Close() - serverName, _, err := net.SplitHostPort(addr) - if err != nil { - return err - } - if err = c.Hello(addr); err != nil { - return err - } - if ok, _ := c.Extension("STARTTLS"); ok { - config := &tls.Config{ServerName: serverName} - config.InsecureSkipVerify = s.EnableSelfSignedCert - if err = c.StartTLS(config); err != nil { - return err - } - } - if auth != nil { - if ok, _ := c.Extension("AUTH"); !ok { - return errors.New("Notifications: server doesn't support AUTH") - } - err = c.Auth(auth) - if err != nil { - return err - } - } - if err = c.Mail(s.Sender); err != nil { - return err - } - for _, addr := range to { - if err = c.Rcpt(addr); err != nil { - return err - } - } - w, err := c.Data() - if err != nil { - return err - } - _, err = w.Write(msg) - if err != nil { - return err - } - err = w.Close() - if err != nil { - return err - } - return c.Quit() -} diff --git a/internal/support/notifications/sending_service_test.go b/internal/support/notifications/sending_service_test.go deleted file mode 100644 index 116fd2a6e2..0000000000 --- a/internal/support/notifications/sending_service_test.go +++ /dev/null @@ -1,136 +0,0 @@ -/******************************************************************************* - * Copyright 2020 Technotects - * - * 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 notifications - -import ( - "fmt" - "github.com/google/uuid" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "testing" -) - -func TestBuildSmtpMessageNoContentType(t *testing.T) { - subject := uuid.New().String() - from := uuid.New().String() - to1 := uuid.New().String() - to2 := uuid.New().String() - - message := uuid.New().String() - - result := buildSmtpMessage(from, subject, []string{to1, to2}, "", message) - - require.NotNil(t, result) - - stringResult := string(result) - - expected := fmt.Sprintf("Subject: %s\r\nFrom: %s\r\nTo: %s,%s\r\n\r\n%s\r\n", subject, from, to1, to2, message) - assert.Equal(t, expected, stringResult) -} - -func TestBuildSmtpMessageContentType(t *testing.T) { - subject := uuid.New().String() - from := uuid.New().String() - to := uuid.New().String() - contentType := uuid.New().String() - message := uuid.New().String() - - result := buildSmtpMessage(from, subject, []string{to}, contentType, message) - - require.NotNil(t, result) - - stringResult := string(result) - - expected := fmt.Sprintf("Subject: %s\r\nFrom: %s\r\nTo: %s\r\nMIME-version: 1.0;\r\nContent-Type: %s; charset=\"UTF-8\";\r\n\r\n%s\r\n", subject, from, to, contentType, message) - assert.Equal(t, expected, stringResult) -} - -func TestBuildSmtpMessageLongMessageIsChunkedIfNeeded(t *testing.T) { - subject := uuid.New().String() - from := uuid.New().String() - to := uuid.New().String() - - message := uuid.New().String() - - for i := 0; i < 5; i++ { - message += message - } - - require.Greater(t, len(message), 998) - require.Less(t, len(message), 1896) - - result := buildSmtpMessage(from, subject, []string{to}, "", message) - - require.NotNil(t, result) - - stringResult := string(result) - - expected := fmt.Sprintf("Subject: %s\r\nFrom: %s\r\nTo: %s\r\n\r\n%s\r\n%s\r\n", subject, from, to, message[0:998], message[998:]) - assert.Equal(t, expected, stringResult) -} - -func TestBuildSmtpMessageLongMessageIsPreChunked(t *testing.T) { - subject := uuid.New().String() - from := uuid.New().String() - to := uuid.New().String() - - longLine := uuid.New().String() - - for i := 0; i < 5; i++ { - longLine += longLine - } - - require.Greater(t, len(longLine), 998) - require.Less(t, len(longLine), 1896) - - formattedMessage := fmt.Sprintf("%s\r\n%s", longLine[0:998], longLine[998:]) - - result := buildSmtpMessage(from, subject, []string{to}, "", formattedMessage) - - require.NotNil(t, result) - - stringResult := string(result) - - expected := fmt.Sprintf("Subject: %s\r\nFrom: %s\r\nTo: %s\r\n\r\n%s\r\n", subject, from, to, formattedMessage) - assert.Equal(t, expected, stringResult) -} - -func TestBuildSmtpMessageLongMessageIsPartlyChunked(t *testing.T) { - subject := uuid.New().String() - from := uuid.New().String() - to := uuid.New().String() - - longLine := uuid.New().String() - - for i := 0; i < 5; i++ { - longLine += longLine - } - - require.Greater(t, len(longLine), 998) - require.Less(t, len(longLine), 1896) - - goodLine := uuid.New().String() + uuid.New().String() + "\r\n" - - formattedMessage := fmt.Sprintf("%s\r\n%s", longLine[0:998], longLine[998:]) - - result := buildSmtpMessage(from, subject, []string{to}, "", goodLine+formattedMessage) - - require.NotNil(t, result) - - stringResult := string(result) - - expected := fmt.Sprintf("Subject: %s\r\nFrom: %s\r\nTo: %s\r\n\r\n%s%s\r\n%s\r\n", subject, from, to, goodLine, longLine[0:998], longLine[998:]) - assert.Equal(t, expected, stringResult) -} diff --git a/internal/support/notifications/utils.go b/internal/support/notifications/utils.go deleted file mode 100644 index 0adcb10726..0000000000 --- a/internal/support/notifications/utils.go +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * Copyright 2018 Dell Technologies Inc. - * - * 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 notifications - -import ( - "fmt" - "io" - "io/ioutil" - - "github.com/edgexfoundry/edgex-go/internal/core/data/errors" - notificationsConfig "github.com/edgexfoundry/edgex-go/internal/support/notifications/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" -) - -const ( - ExceededMaxResultCount string = "error, exceeded the max limit as defined in config" -) - -// Printing function purely for debugging purposes -// Print the body of a request to the console -func printBody(r io.ReadCloser) { - body, err := ioutil.ReadAll(r) - bodyString := string(body) - - if err != nil { - fmt.Println(err) - } - - fmt.Println(bodyString) -} - -func checkMaxLimit(limit int, lc logger.LoggingClient, config notificationsConfig.ConfigurationStruct) error { - if limit > config.Service.MaxResultCount { - lc.Error(ExceededMaxResultCount) - return errors.NewErrLimitExceeded(limit) - } - - return nil -} diff --git a/internal/support/scheduler/const.go b/internal/support/scheduler/const.go deleted file mode 100644 index 8f733d9828..0000000000 --- a/internal/support/scheduler/const.go +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Dell Inc. - * - * 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 scheduler - -const ( - - /* -------------- Constants for Scheduler -------------------- */ - ID = "id" - NAME = "name" - TARGETNAME = "targetname" - INTERVALACTION = "intervalaction" - INTERVAL = "interval" - LABEL = "label" - YAML = "yaml" - COMMAND = "command" - KEY = "key" - VALUE = "value" - UNLOCKED = "UNLOCKED" - ENABLED = "ENABLED" - TIMELAYOUT = "20060102T150405" - SCRUB = "scrub" - TARGET = "target" - - /* ---------------- URL PARAM NAMES -----------------------*/ - ContentTypeKey = "Content-Type" - ContentTypeJsonValue = "application/json; charset=utf-8" - ContentLengthKey = "Content-Length" -) diff --git a/internal/support/scheduler/container/queue.go b/internal/support/scheduler/container/queue.go deleted file mode 100644 index 1a012f6113..0000000000 --- a/internal/support/scheduler/container/queue.go +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * Copyright 2019 Dell Inc. - * - * 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 container - -import ( - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/interfaces" - - "github.com/edgexfoundry/go-mod-bootstrap/v2/di" -) - -// QueueName contains the name of scheduler's SchedulerQueueClient implementation in the DIC. -var QueueName = di.TypeInstanceToName((*interfaces.SchedulerQueueClient)(nil)) - -// QueueFrom helper function queries the DIC and returns scheduler's SchedulerQueueClient implementation. -func QueueFrom(get di.Get) interfaces.SchedulerQueueClient { - return get(QueueName).(interfaces.SchedulerQueueClient) -} diff --git a/internal/support/scheduler/errors/types.go b/internal/support/scheduler/errors/types.go deleted file mode 100644 index 168b1a544e..0000000000 --- a/internal/support/scheduler/errors/types.go +++ /dev/null @@ -1,152 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Inc. - * - * 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 errors - -import ( - "fmt" - "github.com/edgexfoundry/edgex-go/internal/pkg/db" -) - -// Interval -type ErrIntervalNotFound struct { - id string -} - -func (e ErrIntervalNotFound) Error() string { - return fmt.Sprintf("no interval found for id: %s", e.id) -} - -func NewErrIntervalNotFound(id string) error { - return ErrIntervalNotFound{id: id} -} - -type ErrIntervalNameInUse struct { - name string -} - -func (e ErrIntervalNameInUse) Error() string { - return fmt.Sprintf("interval name: %s in use", e.name) -} - -func NewErrIntervalNameInUse(name string) error { - return ErrIntervalNameInUse{name: name} -} - -type ErrIntervalStillUsedByIntervalActions struct { - name string -} - -func (e ErrIntervalStillUsedByIntervalActions) Error() string { - return fmt.Sprintf("interval still in use by intervalAction(s) name: %s", e.name) -} - -func NewErrIntervalStillInUse(name string) error { - return ErrIntervalStillUsedByIntervalActions{name: name} -} - -// IntervalAction -type ErrIntervalActionNotFound struct { - id string -} - -func (e ErrIntervalActionNotFound) Error() string { - return fmt.Sprintf("no intervalAction found with id: %s", e.id) -} - -func NewErrIntervalActionNotFound(id string) error { - return ErrIntervalActionNotFound{id: id} -} - -type ErrIntervalActionTargetNameRequired struct { - id string -} - -func (e ErrIntervalActionTargetNameRequired) Error() string { - return fmt.Sprintf("intervalAction [ %s ] requires a target none provided. ", e.id) -} - -func NewErrIntervalActionTargetNameRequired(id string) error { - return ErrIntervalActionTargetNameRequired{id: id} -} - -type ErrIntervalActionNameInUse struct { - name string -} - -func (e ErrIntervalActionNameInUse) Error() string { - return fmt.Sprintf("intervalAction name: %s in use", e.name) -} - -func NewErrIntervalActionNameInUse(name string) error { - return ErrIntervalActionNameInUse{name: name} -} - -type ErrInvalidTimeFormat struct { - value string -} - -func (e ErrInvalidTimeFormat) Error() string { - return fmt.Sprintf("invalid time format for value: %s", e.value) -} - -func NewErrInvalidTimeFormat(value string) error { - return ErrInvalidTimeFormat{value: value} -} - -type ErrInvalidFrequencyFormat struct { - frequency string -} - -func (e ErrInvalidFrequencyFormat) Error() string { - return fmt.Sprintf("invalid frequency format for value: %s", e.frequency) -} - -func NewErrInvalidFrequencyFormat(frequency string) error { - return ErrInvalidFrequencyFormat{frequency: frequency} -} - -type ErrInvalidCronFormat struct { - cron string -} - -func (e ErrInvalidCronFormat) Error() string { - return fmt.Sprintf("invalid cron format for value: %s", e.cron) -} - -func NewErrInvalidCronFormat(cron string) error { - return ErrInvalidCronFormat{cron: cron} -} - -type ErrDbNotFound struct { -} - -func (e ErrDbNotFound) Error() string { - return db.ErrNotFound.Error() -} - -func NewErrDbNotFound() error { - return ErrDbNotFound{} -} - -type ErrLimitExceeded struct { - limit int -} - -func (e ErrLimitExceeded) Error() string { - return fmt.Sprintf("number of records %d exceeds configured max limit", e.limit) -} - -func NewErrLimitExceeded(limit int) error { - return ErrLimitExceeded{limit: limit} -} diff --git a/internal/support/scheduler/init.go b/internal/support/scheduler/init.go index 5adc9bfc08..fa9ddd7059 100644 --- a/internal/support/scheduler/init.go +++ b/internal/support/scheduler/init.go @@ -17,11 +17,9 @@ package scheduler import ( "context" - "fmt" "sync" "time" - "github.com/edgexfoundry/edgex-go/internal/pkg/bootstrap/container" schedulerContainer "github.com/edgexfoundry/edgex-go/internal/support/scheduler/container" "github.com/edgexfoundry/edgex-go/internal/support/scheduler/v2" "github.com/edgexfoundry/edgex-go/internal/support/scheduler/v2/application" @@ -49,26 +47,11 @@ func NewBootstrap(router *mux.Router) *Bootstrap { // BootstrapHandler fulfills the BootstrapHandler contract and performs initialization needed by the scheduler service. func (b *Bootstrap) BootstrapHandler(ctx context.Context, wg *sync.WaitGroup, _ startup.Timer, dic *di.Container) bool { - loadRestRoutes(b.router, dic) v2.LoadRestRoutes(b.router, dic) lc := bootstrapContainer.LoggingClientFrom(dic.Get) configuration := schedulerContainer.ConfigurationFrom(dic.Get) - // add dependencies to bootstrapContainer - scClient := NewSchedulerQueueClient(lc) - dic.Update(di.ServiceConstructorMap{ - schedulerContainer.QueueName: func(get di.Get) interface{} { - return scClient - }, - }) - - err := LoadScheduler(lc, container.DBClientFrom(dic.Get), scClient, configuration) - if err != nil { - lc.Error(fmt.Sprintf("Failed to load schedules and events %s", err.Error())) - return false - } - ticker := time.NewTicker(time.Duration(configuration.ScheduleIntervalTime) * time.Millisecond) StartTicker(ticker, lc, configuration) @@ -80,7 +63,7 @@ func (b *Bootstrap) BootstrapHandler(ctx context.Context, wg *sync.WaitGroup, _ }, }) - err = application.LoadIntervalToSchedulerManager(dic) + err := application.LoadIntervalToSchedulerManager(dic) if err != nil { lc.Errorf("Failed to load interval to scheduler %v", err) return false diff --git a/internal/support/scheduler/interfaces/db.go b/internal/support/scheduler/interfaces/db.go deleted file mode 100644 index e4b9588cd8..0000000000 --- a/internal/support/scheduler/interfaces/db.go +++ /dev/null @@ -1,82 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Inc. - * - * 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 interfaces - -import ( - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type DBClient interface { - CloseSession() - - // **************************** INTERVAL ************************************ - - // Return all the Interval(s) - Intervals() ([]contract.Interval, error) - - // Return Interval(s) up to the number specified - IntervalsWithLimit(limit int) ([]contract.Interval, error) - - // Return Interval by name - IntervalByName(name string) (contract.Interval, error) - - // Return interval by contract id - IntervalById(id string) (contract.Interval, error) - - // Add a new Interval - AddInterval(interval contract.Interval) (string, error) - - // Update an Interval - UpdateInterval(interval contract.Interval) error - - // Remove Interval by id - DeleteIntervalById(id string) error - - // ************************* INTERVAL ACTIONS ******************************* - - // Get all IntervalAction(s) - IntervalActions() ([]contract.IntervalAction, error) - - // Return IntervalAction(s) up to the number specified - IntervalActionsWithLimit(limit int) ([]contract.IntervalAction, error) - - // Get all IntervalAction(s) by interval name - IntervalActionsByIntervalName(name string) ([]contract.IntervalAction, error) - - // Get all IntervalAction(s) by target name - IntervalActionsByTarget(name string) ([]contract.IntervalAction, error) - - // Get IntervalAction by id - IntervalActionById(id string) (contract.IntervalAction, error) - - // Get IntervalAction by name - IntervalActionByName(name string) (contract.IntervalAction, error) - - // Add IntervalAction - AddIntervalAction(intervalAction contract.IntervalAction) (string, error) - - // Update IntervalAction - UpdateIntervalAction(intervalAction contract.IntervalAction) error - - // Remove IntervalAction by id - DeleteIntervalActionById(id string) error - - // ************************** UTILITY FUNCTION(S) *************************** - - // Scrub all scheduler interval actions from the database data (only used in test) - ScrubAllIntervalActions() (int, error) - - // Scrub all scheduler intervals from the database (only used in test) - ScrubAllIntervals() (int, error) -} diff --git a/internal/support/scheduler/interfaces/mocks/DBClient.go b/internal/support/scheduler/interfaces/mocks/DBClient.go deleted file mode 100644 index 48f0f21c86..0000000000 --- a/internal/support/scheduler/interfaces/mocks/DBClient.go +++ /dev/null @@ -1,378 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// DBClient is an autogenerated mock type for the DBClient type -type DBClient struct { - mock.Mock -} - -// AddInterval provides a mock function with given fields: interval -func (_m *DBClient) AddInterval(interval models.Interval) (string, error) { - ret := _m.Called(interval) - - var r0 string - if rf, ok := ret.Get(0).(func(models.Interval) string); ok { - r0 = rf(interval) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.Interval) error); ok { - r1 = rf(interval) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AddIntervalAction provides a mock function with given fields: intervalAction -func (_m *DBClient) AddIntervalAction(intervalAction models.IntervalAction) (string, error) { - ret := _m.Called(intervalAction) - - var r0 string - if rf, ok := ret.Get(0).(func(models.IntervalAction) string); ok { - r0 = rf(intervalAction) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.IntervalAction) error); ok { - r1 = rf(intervalAction) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// CloseSession provides a mock function with given fields: -func (_m *DBClient) CloseSession() { - _m.Called() -} - -// DeleteIntervalActionById provides a mock function with given fields: id -func (_m *DBClient) DeleteIntervalActionById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteIntervalById provides a mock function with given fields: id -func (_m *DBClient) DeleteIntervalById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// IntervalActionById provides a mock function with given fields: id -func (_m *DBClient) IntervalActionById(id string) (models.IntervalAction, error) { - ret := _m.Called(id) - - var r0 models.IntervalAction - if rf, ok := ret.Get(0).(func(string) models.IntervalAction); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.IntervalAction) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalActionByName provides a mock function with given fields: name -func (_m *DBClient) IntervalActionByName(name string) (models.IntervalAction, error) { - ret := _m.Called(name) - - var r0 models.IntervalAction - if rf, ok := ret.Get(0).(func(string) models.IntervalAction); ok { - r0 = rf(name) - } else { - r0 = ret.Get(0).(models.IntervalAction) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalActions provides a mock function with given fields: -func (_m *DBClient) IntervalActions() ([]models.IntervalAction, error) { - ret := _m.Called() - - var r0 []models.IntervalAction - if rf, ok := ret.Get(0).(func() []models.IntervalAction); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.IntervalAction) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalActionsByIntervalName provides a mock function with given fields: name -func (_m *DBClient) IntervalActionsByIntervalName(name string) ([]models.IntervalAction, error) { - ret := _m.Called(name) - - var r0 []models.IntervalAction - if rf, ok := ret.Get(0).(func(string) []models.IntervalAction); ok { - r0 = rf(name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.IntervalAction) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalActionsByTarget provides a mock function with given fields: name -func (_m *DBClient) IntervalActionsByTarget(name string) ([]models.IntervalAction, error) { - ret := _m.Called(name) - - var r0 []models.IntervalAction - if rf, ok := ret.Get(0).(func(string) []models.IntervalAction); ok { - r0 = rf(name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.IntervalAction) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalActionsWithLimit provides a mock function with given fields: limit -func (_m *DBClient) IntervalActionsWithLimit(limit int) ([]models.IntervalAction, error) { - ret := _m.Called(limit) - - var r0 []models.IntervalAction - if rf, ok := ret.Get(0).(func(int) []models.IntervalAction); ok { - r0 = rf(limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.IntervalAction) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalById provides a mock function with given fields: id -func (_m *DBClient) IntervalById(id string) (models.Interval, error) { - ret := _m.Called(id) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalByName provides a mock function with given fields: name -func (_m *DBClient) IntervalByName(name string) (models.Interval, error) { - ret := _m.Called(name) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(name) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Intervals provides a mock function with given fields: -func (_m *DBClient) Intervals() ([]models.Interval, error) { - ret := _m.Called() - - var r0 []models.Interval - if rf, ok := ret.Get(0).(func() []models.Interval); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Interval) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalsWithLimit provides a mock function with given fields: limit -func (_m *DBClient) IntervalsWithLimit(limit int) ([]models.Interval, error) { - ret := _m.Called(limit) - - var r0 []models.Interval - if rf, ok := ret.Get(0).(func(int) []models.Interval); ok { - r0 = rf(limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Interval) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ScrubAllIntervalActions provides a mock function with given fields: -func (_m *DBClient) ScrubAllIntervalActions() (int, error) { - ret := _m.Called() - - var r0 int - if rf, ok := ret.Get(0).(func() int); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int) - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ScrubAllIntervals provides a mock function with given fields: -func (_m *DBClient) ScrubAllIntervals() (int, error) { - ret := _m.Called() - - var r0 int - if rf, ok := ret.Get(0).(func() int); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int) - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateInterval provides a mock function with given fields: interval -func (_m *DBClient) UpdateInterval(interval models.Interval) error { - ret := _m.Called(interval) - - var r0 error - if rf, ok := ret.Get(0).(func(models.Interval) error); ok { - r0 = rf(interval) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateIntervalAction provides a mock function with given fields: intervalAction -func (_m *DBClient) UpdateIntervalAction(intervalAction models.IntervalAction) error { - ret := _m.Called(intervalAction) - - var r0 error - if rf, ok := ret.Get(0).(func(models.IntervalAction) error); ok { - r0 = rf(intervalAction) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/internal/support/scheduler/interfaces/mocks/SchedulerQueueClient.go b/internal/support/scheduler/interfaces/mocks/SchedulerQueueClient.go deleted file mode 100644 index 31bf1295cd..0000000000 --- a/internal/support/scheduler/interfaces/mocks/SchedulerQueueClient.go +++ /dev/null @@ -1,200 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// SchedulerQueueClient is an autogenerated mock type for the SchedulerQueueClient type -type SchedulerQueueClient struct { - mock.Mock -} - -// AddIntervalActionToQueue provides a mock function with given fields: intervalAction -func (_m *SchedulerQueueClient) AddIntervalActionToQueue(intervalAction models.IntervalAction) error { - ret := _m.Called(intervalAction) - - var r0 error - if rf, ok := ret.Get(0).(func(models.IntervalAction) error); ok { - r0 = rf(intervalAction) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// AddIntervalToQueue provides a mock function with given fields: interval -func (_m *SchedulerQueueClient) AddIntervalToQueue(interval models.Interval) error { - ret := _m.Called(interval) - - var r0 error - if rf, ok := ret.Get(0).(func(models.Interval) error); ok { - r0 = rf(interval) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Connect provides a mock function with given fields: -func (_m *SchedulerQueueClient) Connect() (string, error) { - ret := _m.Called() - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// QueryIntervalActionByID provides a mock function with given fields: intervalActionId -func (_m *SchedulerQueueClient) QueryIntervalActionByID(intervalActionId string) (models.IntervalAction, error) { - ret := _m.Called(intervalActionId) - - var r0 models.IntervalAction - if rf, ok := ret.Get(0).(func(string) models.IntervalAction); ok { - r0 = rf(intervalActionId) - } else { - r0 = ret.Get(0).(models.IntervalAction) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(intervalActionId) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// QueryIntervalActionByName provides a mock function with given fields: intervalActionName -func (_m *SchedulerQueueClient) QueryIntervalActionByName(intervalActionName string) (models.IntervalAction, error) { - ret := _m.Called(intervalActionName) - - var r0 models.IntervalAction - if rf, ok := ret.Get(0).(func(string) models.IntervalAction); ok { - r0 = rf(intervalActionName) - } else { - r0 = ret.Get(0).(models.IntervalAction) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(intervalActionName) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// QueryIntervalByID provides a mock function with given fields: intervalId -func (_m *SchedulerQueueClient) QueryIntervalByID(intervalId string) (models.Interval, error) { - ret := _m.Called(intervalId) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(intervalId) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(intervalId) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// QueryIntervalByName provides a mock function with given fields: intervalName -func (_m *SchedulerQueueClient) QueryIntervalByName(intervalName string) (models.Interval, error) { - ret := _m.Called(intervalName) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(intervalName) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(intervalName) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RemoveIntervalActionQueue provides a mock function with given fields: intervalActionId -func (_m *SchedulerQueueClient) RemoveIntervalActionQueue(intervalActionId string) error { - ret := _m.Called(intervalActionId) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(intervalActionId) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// RemoveIntervalInQueue provides a mock function with given fields: intervalId -func (_m *SchedulerQueueClient) RemoveIntervalInQueue(intervalId string) error { - ret := _m.Called(intervalId) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(intervalId) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateIntervalActionQueue provides a mock function with given fields: intervalAction -func (_m *SchedulerQueueClient) UpdateIntervalActionQueue(intervalAction models.IntervalAction) error { - ret := _m.Called(intervalAction) - - var r0 error - if rf, ok := ret.Get(0).(func(models.IntervalAction) error); ok { - r0 = rf(intervalAction) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateIntervalInQueue provides a mock function with given fields: interval -func (_m *SchedulerQueueClient) UpdateIntervalInQueue(interval models.Interval) error { - ret := _m.Called(interval) - - var r0 error - if rf, ok := ret.Get(0).(func(models.Interval) error); ok { - r0 = rf(interval) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/internal/support/scheduler/interfaces/scheduler_queue.go b/internal/support/scheduler/interfaces/scheduler_queue.go deleted file mode 100644 index dcaf1384f6..0000000000 --- a/internal/support/scheduler/interfaces/scheduler_queue.go +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Inc. - * - * 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 interfaces - -import ( - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type SchedulerQueueClient interface { - - // **************************** INTERVAL ************************************ - - // Return Interval by ID from the Scheduler Interval Context - QueryIntervalByID(intervalId string) (contract.Interval, error) - - // Return Interval by Name from the Scheduler Interval Context - QueryIntervalByName(intervalName string) (contract.Interval, error) - - // Add Interval into the Scheduler Queue - AddIntervalToQueue(interval contract.Interval) error - - // Update Interval in the Scheduler Queue - UpdateIntervalInQueue(interval contract.Interval) error - - // Remote the Interval from the Scheduler Queue - RemoveIntervalInQueue(intervalId string) error - - // ************************* INTERVAL ACTIONS ******************************* - - // Return IntervalAction by ID from the Scheduler IntervalAction Context - QueryIntervalActionByID(intervalActionId string) (contract.IntervalAction, error) - - // Return IntervalAction by Name from the Scheduler IntervalAction Context - QueryIntervalActionByName(intervalActionName string) (contract.IntervalAction, error) - - // Add IntervalAction into Scheduler Queue - AddIntervalActionToQueue(intervalAction contract.IntervalAction) error - - // Update IntervalAction in the Scheduler Queue - UpdateIntervalActionQueue(intervalAction contract.IntervalAction) error - - // Remove IntervalAction from the Scheduler Queue - RemoveIntervalActionQueue(intervalActionId string) error - - // Check if we can connect to Scheduler Queue - Connect() (string, error) -} diff --git a/internal/support/scheduler/interval.go b/internal/support/scheduler/interval.go deleted file mode 100644 index 77986f3a48..0000000000 --- a/internal/support/scheduler/interval.go +++ /dev/null @@ -1,105 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Inc. - * - * 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 scheduler - -import ( - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/errors" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/interfaces" -) - -func getIntervals(limit int, dbClient interfaces.DBClient) ([]contract.Interval, error) { - var err error - var intervals []contract.Interval - - if limit <= 0 { - intervals, err = dbClient.Intervals() - } else { - intervals, err = dbClient.IntervalsWithLimit(limit) - } - - if err != nil { - return nil, err - } - - return intervals, err -} - -func addNewInterval( - interval contract.Interval, - dbClient interfaces.DBClient, - scClient interfaces.SchedulerQueueClient) (string, error) { - - name := interval.Name - - // Check if the name is unique - ret, err := dbClient.IntervalByName(name) - if err == nil && ret.Name == name { - return "", errors.NewErrIntervalNameInUse(name) - } - - // Validate the Start time format - start := interval.Start - if start != "" { - if _, err := msToTime(start); err != nil { - return "", errors.NewErrInvalidTimeFormat(start) - } - } - // Validate the End time format - end := interval.End - if end != "" { - if _, err := msToTime(end); err != nil { - return "", errors.NewErrInvalidTimeFormat(end) - } - } - // Validate the Frequency - freq := interval.Frequency - if freq != "" { - _, err := parseFrequency(freq) - if err != nil { - return "", errors.NewErrInvalidFrequencyFormat(freq) - } - } - - // Add the new interval to the database - ID, err := dbClient.AddInterval(interval) - if err != nil { - return "", err - } - - // Push the new interval into scheduler queue - interval.ID = ID - err = scClient.AddIntervalToQueue(interval) - if err != nil { - return "", err - } - - return ID, nil -} - -func getIntervalByName(name string, dbClient interfaces.DBClient) (interval contract.Interval, err error) { - interval, err = dbClient.IntervalByName(name) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrIntervalNotFound(name) - } - - return contract.Interval{}, err - } - - return interval, nil -} diff --git a/internal/support/scheduler/interval_action.go b/internal/support/scheduler/interval_action.go deleted file mode 100644 index b9c26395d5..0000000000 --- a/internal/support/scheduler/interval_action.go +++ /dev/null @@ -1,321 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Inc. - * - * 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 scheduler - -import ( - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/errors" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/interfaces" -) - -func addNewIntervalAction( - intervalAction contract.IntervalAction, - dbClient interfaces.DBClient, - scClient interfaces.SchedulerQueueClient) (string, error) { - - name := intervalAction.Name - - // Validate the IntervalAction is not in use - ret, err := dbClient.IntervalActionByName(name) - if err == nil && ret.Name == name { - return "", errors.NewErrIntervalActionNameInUse(name) - } - - // Validate the Target - target := intervalAction.Target - if target == "" { - return "", errors.NewErrIntervalActionTargetNameRequired(intervalAction.ID) - } - - // Validate the Interval - interval := intervalAction.Interval - if interval != "" { - _, err := dbClient.IntervalByName(interval) - if err != nil { - return "", errors.NewErrIntervalNotFound(interval) - } - } else { - return "", errors.NewErrIntervalNotFound(intervalAction.ID) - } - - // Validate the IntervalAction does not exist in the scheduler queue - retQ, err := scClient.QueryIntervalActionByName(name) - if err == nil && retQ.Name == name { - return "", errors.NewErrIntervalActionNameInUse(name) - } - - // Add the new Interval Action to the database - ID, err := dbClient.AddIntervalAction(intervalAction) - if err != nil { - return "", err - } - - intervalAction.ID = ID - - // Add the new IntervalAction into scheduler queue - err = scClient.AddIntervalActionToQueue(intervalAction) - if err != nil { - return "", err - } - - return ID, nil -} - -func updateIntervalAction( - from contract.IntervalAction, - dbClient interfaces.DBClient, - scClient interfaces.SchedulerQueueClient) error { - - to, err := dbClient.IntervalActionById(from.ID) - if err != nil { - // check by name - _, err := dbClient.IntervalActionByName(from.Name) - if err != nil { - return errors.NewErrIntervalNotFound(from.ID) - } - } - // Validate interval - interval := from.Interval - if interval != "" { - _, err := dbClient.IntervalByName(interval) - if err != nil { - return errors.NewErrIntervalNotFound(interval) - } - } - if interval != to.Interval { - to.Interval = interval - } - - // Name - name := from.Name - if name == "" { - return errors.NewErrIntervalActionTargetNameRequired("") - } - // Ensure name is unique - if name != to.Name { - ret, err := dbClient.IntervalActionByName(name) - if err == nil && ret.Name == name { - return errors.NewErrIntervalActionNameInUse(name) - } - to.Name = name - } - - // Validate target - target := from.Target - if target == "" { - return errors.NewErrIntervalActionTargetNameRequired(from.ID) - } - if target != to.Target { - to.Target = target - } - // Topic - topic := from.Topic - if topic != "" { - to.Topic = from.Topic - } - // User - user := from.User - if user != to.User { - to.User = user - } - // Publisher - pub := from.Publisher - if pub != to.Publisher { - to.Publisher = pub - } - // Password - pass := from.Password - if pass != to.Password { - to.Password = pass - } - // Port - // TODO: Do we need a reasonable port restriction here? - port := from.Port - if port != to.Port { - to.Port = port - } - // Address - // TODO: Do we need a regex on a valid path sequence? - address := from.Address - if address != to.Address { - to.Address = address - } - // HTTPMethod - // TODO: Valid set of HTTP Verbs - method := from.HTTPMethod - if method != to.HTTPMethod { - to.HTTPMethod = method - } - // Protocol - // TODO: Valid protocol constraint? - protocol := from.Protocol - if protocol != to.Protocol { - to.Protocol = protocol - } - // Parameters - params := from.Parameters - if params != to.Parameters { - to.Parameters = params - } - - // Validate the IntervalAction does not exist in the scheduler queue - _, err = scClient.QueryIntervalActionByName(to.Name) - if err == nil { - // it's found we need to really update it - err = scClient.UpdateIntervalActionQueue(to) - if err != nil { - return errors.NewErrIntervalActionNotFound(to.Name) - } - } - return dbClient.UpdateIntervalAction(to) -} - -func getIntervalActionById(id string, dbClient interfaces.DBClient) (contract.IntervalAction, error) { - intervalAction, err := dbClient.IntervalActionById(id) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrIntervalActionNotFound(id) - } - return contract.IntervalAction{}, err - } - return intervalAction, nil -} - -func getIntervalActions(limit int, dbClient interfaces.DBClient) ([]contract.IntervalAction, error) { - var err error - var intervalActions []contract.IntervalAction - - if limit <= 0 { - intervalActions, err = dbClient.IntervalActions() - } else { - intervalActions, err = dbClient.IntervalActionsWithLimit(limit) - } - - if err != nil { - return nil, err - } - - return intervalActions, err -} - -func getIntervalActionByName(name string, dbClient interfaces.DBClient) (contract.IntervalAction, error) { - intervalAction, err := dbClient.IntervalActionByName(name) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrIntervalActionNotFound(name) - } - return contract.IntervalAction{}, err - } - return intervalAction, nil -} - -func getIntervalActionsByTarget(target string, dbClient interfaces.DBClient) ([]contract.IntervalAction, error) { - intervalActions, err := dbClient.IntervalActionsByTarget(target) - if err != nil { - return []contract.IntervalAction{}, err - } - return intervalActions, err -} - -func getIntervalActionsByInterval(interval string, dbClient interfaces.DBClient) ([]contract.IntervalAction, error) { - intervalActions, err := dbClient.IntervalActionsByIntervalName(interval) - if err != nil { - return []contract.IntervalAction{}, err - } - return intervalActions, err -} - -func deleteIntervalActionById( - id string, - dbClient interfaces.DBClient, - scClient interfaces.SchedulerQueueClient) error { - - // check in memory first - inMemory, err := scClient.QueryIntervalActionByID(id) - if err != nil { - return errors.NewErrIntervalNotFound(id) - } - // remove in memory - err = scClient.RemoveIntervalActionQueue(inMemory.ID) - if err != nil { - return errors.NewErrDbNotFound() - } - - // check in DB - intervalAction, err := getIntervalActionById(id, dbClient) - if err != nil { - if err == db.ErrNotFound { - return errors.NewErrIntervalNotFound(intervalAction.Name) - } else { - return err - } - } - - // remove from DB - if err = deleteIntervalAction(intervalAction, dbClient); err != nil { - return err - } - return nil -} - -func deleteIntervalActionByName( - name string, - dbClient interfaces.DBClient, - scClient interfaces.SchedulerQueueClient) error { - - // check in memory first - inMemory, err := scClient.QueryIntervalActionByName(name) - if err != nil { - return errors.NewErrIntervalNotFound(name) - } - // remove in memory - err = scClient.RemoveIntervalActionQueue(inMemory.ID) - if err != nil { - return errors.NewErrDbNotFound() - } - - intervalAction, err := getIntervalActionByName(name, dbClient) - if err != nil { - if err == db.ErrNotFound { - return errors.NewErrIntervalNotFound(intervalAction.Name) - } else { - return err - } - } - if err = deleteIntervalAction(intervalAction, dbClient); err != nil { - return err - } - return nil -} - -func deleteIntervalAction(intervalAction contract.IntervalAction, dbClient interfaces.DBClient) error { - if err := dbClient.DeleteIntervalActionById(intervalAction.ID); err != nil { - return err - } - return nil -} - -func scrubAllInteralActions(lc logger.LoggingClient, dbClient interfaces.DBClient) (int, error) { - lc.Info("Scrubbing All IntervalAction(s).") - - count, err := dbClient.ScrubAllIntervalActions() - if err != nil { - return 0, err - } - return count, nil -} diff --git a/internal/support/scheduler/interval_action_test.go b/internal/support/scheduler/interval_action_test.go deleted file mode 100644 index fe987de6c0..0000000000 --- a/internal/support/scheduler/interval_action_test.go +++ /dev/null @@ -1,197 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Inc. - * - * 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 scheduler - -import ( - "testing" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/pkg/errors" - "github.com/stretchr/testify/mock" - - dbMock "github.com/edgexfoundry/edgex-go/internal/support/scheduler/interfaces/mocks" -) - -func newGetIntervalActionsWithLimitMockDB(expectedLimit int) *dbMock.DBClient { - myMock := &dbMock.DBClient{} - - myMock.On("IntervalActionsWithLimit", mock.MatchedBy(func(limit int) bool { - return limit == expectedLimit - })).Return(func(limit int) []models.IntervalAction { - intervalActions := make([]models.IntervalAction, 0) - for i := 0; i < limit; i++ { - intervalActions = append(intervalActions, testIntervalAction) - } - return intervalActions - }, nil) - - return myMock -} - -func TestGetIntervalActionsWithLimit(t *testing.T) { - reset() - - limit := 1 - myMock := newGetIntervalActionsWithLimitMockDB(limit) - - intervalActions, err := getIntervalActions(limit, myMock) - if err != nil { - t.Fatalf(err.Error()) - } - - if len(intervalActions) != limit { - t.Fatalf("expected %d event", limit) - } - - myMock.AssertExpectations(t) -} - -func TestGetIntervalActions(t *testing.T) { - reset() - myMock := &dbMock.DBClient{} - - myMock.On("IntervalActions").Return([]models.IntervalAction{testIntervalAction}, nil) - - intervalActions, err := getIntervalActions(0, myMock) - if err != nil { - t.Fatalf(err.Error()) - } - - if len(intervalActions) == 0 { - t.Fatalf("no actions found") - } - - if len(intervalActions) != 1 { - t.Fatalf("expected 1 event") - } -} - -func TestGetIntervalActionsByIntervalName(t *testing.T) { - reset() - myMock := &dbMock.DBClient{} - - myMock.On("IntervalActionsByIntervalName", - mock.MatchedBy( - func(name string) bool { - return name == testIntervalAction.Interval - })).Return([]models.IntervalAction{testIntervalAction}, nil) - - intervalActions, err := getIntervalActionsByInterval(testIntervalActionInterval, myMock) - if err != nil { - t.Fatalf(err.Error()) - } - if len(intervalActions) == 0 { - t.Fatalf("no interval action(s) found") - } - if len(intervalActions) != 1 { - t.Fatalf("expected 1 event") - } -} - -func TestGetIntervalActionByName(t *testing.T) { - reset() - myMock := &dbMock.DBClient{} - - myMock.On("IntervalActionByName", - mock.MatchedBy(func(name string) bool { return name == testIntervalAction.Name })).Return(testIntervalAction, nil) - - intervalAction, err := getIntervalActionByName(testIntervalActionName, myMock) - if err != nil { - t.Fatalf(err.Error()) - } - if len(intervalAction.Name) == 0 { - t.Fatalf("no interval action found") - } - if intervalAction.Name != testIntervalActionName { - t.Fatalf("incorrect interval action name found") - } -} - -func TestGetIntervalActionById(t *testing.T) { - reset() - myMock := &dbMock.DBClient{} - - myMock.On("IntervalActionById", - mock.MatchedBy(func(id string) bool { return id == testIntervalAction.ID })).Return(testIntervalAction, nil) - - intervalAction, err := getIntervalActionById(testUUIDString, myMock) - if err != nil { - t.Fatalf(err.Error()) - } - if len(intervalAction.ID) == 0 { - t.Fatalf("no interval action found") - } - if intervalAction.ID != testUUIDString { - t.Fatalf("incorrect UUID found") - } -} - -func TestUpdateIntervalAction(t *testing.T) { - reset() - myMock := &dbMock.DBClient{} - mySchedulerMock := &dbMock.SchedulerQueueClient{} - - // Validation call - myMock.On("IntervalActionById", - mock.Anything).Return(models.IntervalAction{Name: testIntervalActionName}, nil) - - myMock.On("IntervalByName", - mock.Anything).Return(models.Interval{}, nil) - - // Update IntervalAction call - myMock.On("UpdateIntervalAction", - mock.Anything).Return(nil) - - mySchedulerMock.On("QueryIntervalActionByName", - mock.Anything).Return(models.IntervalAction{}, errors.New("mock db not found")) - - nIntervalAction := models.IntervalAction{Name: testIntervalActionName, Target: testIntervalActionTarget, Origin: testOrigin, Interval: testIntervalActionInterval} - - err := updateIntervalAction(nIntervalAction, myMock, mySchedulerMock) - if err != nil { - t.Fatalf(err.Error()) - } - - myMock.AssertExpectations(t) -} - -func TestDeleteIntervalActionById(t *testing.T) { - reset() - - myMock := &dbMock.DBClient{} - mySchedulerMock := &dbMock.SchedulerQueueClient{} - - // Validation call - myMock.On("IntervalActionById", - mock.MatchedBy(func(id string) bool { return id == testIntervalAction.ID })).Return(testIntervalAction, nil) - - // remove the IntervalAction from DB - myMock.On("DeleteIntervalActionById", - mock.Anything).Return(nil) - - // Queue Validation - mySchedulerMock.On("QueryIntervalActionByID", - mock.Anything).Return(models.IntervalAction{}, nil) - - // remove the IntervalAction from memory - mySchedulerMock.On("RemoveIntervalActionQueue", - mock.Anything).Return(nil) - - err := deleteIntervalActionById(testUUIDString, myMock, mySchedulerMock) - if err != nil { - t.Fatalf(err.Error()) - } - myMock.AssertExpectations(t) - mySchedulerMock.AssertExpectations(t) -} diff --git a/internal/support/scheduler/interval_test.go b/internal/support/scheduler/interval_test.go deleted file mode 100644 index 32ff83526d..0000000000 --- a/internal/support/scheduler/interval_test.go +++ /dev/null @@ -1,174 +0,0 @@ -// ******************************************************************************* -// * Copyright 2018 Dell Inc. -// * -// * 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 scheduler - -import ( - "testing" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/stretchr/testify/mock" - - errorsSched "github.com/edgexfoundry/edgex-go/internal/support/scheduler/errors" - dbMock "github.com/edgexfoundry/edgex-go/internal/support/scheduler/interfaces/mocks" -) - -var testInterval models.Interval -var testIntervalAction models.IntervalAction -var testTimestamps models.Timestamps - -const ( - testIntervalName string = "midnight" - testInterNewName string = "noon" - testOrigin int64 = 123456789 - testUUIDString string = "ca93c8fa-9919-4ec5-85d3-f81b2b6a7bc1" - testIntervalActionId string = "ca93c8fa-9919-4ec5-85d3-f81b2b6a7bc1" - - testIntervalActionName string = "scrub-aged-events" - testIntervalActionNewName string = "scub-bub" - testIntervalActionTarget string = "core-data" - testIntervalActionInterval string = "midnight" -) - -// Supporting methods -// Reset() re-initializes dependencies for each test -func reset() { - testTimestamps = models.Timestamps{Origin: testOrigin} - testInterval.ID = testUUIDString - testInterval.Timestamps = testTimestamps - testInterval.Name = testIntervalName - - testIntervalAction.ID = testUUIDString - testIntervalAction.Name = testIntervalActionName - testIntervalAction.Target = testIntervalActionTarget - testIntervalAction.Interval = testIntervalActionInterval -} - -func newGetIntervalsWithLimitMockDB(expectedLimit int) *dbMock.DBClient { - myMock := &dbMock.DBClient{} - - myMock.On("IntervalsWithLimit", mock.MatchedBy(func(limit int) bool { - return limit == expectedLimit - })).Return(func(limit int) []models.Interval { - intervals := make([]models.Interval, 0) - for i := 0; i < limit; i++ { - intervals = append(intervals, testInterval) - } - return intervals - }, nil) - - return myMock -} - -func TestGetIntervalsWithLimit(t *testing.T) { - reset() - - limit := 1 - myMock := newGetIntervalsWithLimitMockDB(limit) - - intervals, err := getIntervals(limit, myMock) - if err != nil { - t.Fatalf(err.Error()) - } - - if len(intervals) != limit { - t.Fatalf("expected %d interval", limit) - } - - myMock.AssertExpectations(t) -} - -func TestIntervalBylName(t *testing.T) { - reset() - myMock := &dbMock.DBClient{} - - myMock.On("IntervalByName", - mock.MatchedBy(func(name string) bool { return name == testInterval.Name })).Return(testInterval, nil) - - interval, err := getIntervalByName(testInterval.Name, myMock) - if err != nil { - t.Fatalf(err.Error()) - } - - if interval.Name != testInterval.Name { - t.Fatalf("expected interval name to be the same") - } -} - -func TestAddIntervalFailOnExistingName(t *testing.T) { - reset() - myMock := &dbMock.DBClient{} - mySchedulerMock := &dbMock.SchedulerQueueClient{} - - // Validation Call - myMock.On("IntervalByName", - mock.Anything).Return(testInterval, nil) - - // Add Interval Call - myMock.On("AddInterval", - mock.Anything).Return(testUUIDString, nil) - - // Scheduler call - mySchedulerMock.On("AddIntervalToQueue", - mock.Anything).Return(nil) - - // Scheduler call - mySchedulerMock.On("QueryIntervalByName", - mock.Anything).Return(models.Interval{}, nil) - - nInterval := models.Interval{Name: testInterval.Name, Timestamps: testTimestamps} - - _, err := addNewInterval(nInterval, myMock, mySchedulerMock) - if err != nil { - switch err.(type) { - case errorsSched.ErrIntervalNameInUse: - // expected - default: - t.Fatalf("Expected errors.ErrIntervalNameInUse") - } - } -} - -func TestAddIntervalFailOnInvalidTimeFormat(t *testing.T) { - reset() - myMock := &dbMock.DBClient{} - mySchedulerMock := &dbMock.SchedulerQueueClient{} - - // Validation Call - myMock.On("IntervalByName", - mock.Anything).Return(models.Interval{}, nil) - - // Add Interval Call - myMock.On("AddInterval", - mock.Anything).Return(testUUIDString, nil) - - // Scheduler call - mySchedulerMock.On("AddIntervalToQueue", - mock.Anything).Return(nil) - - mySchedulerMock.On("QueryIntervalByName", - mock.Anything).Return(models.Interval{}, nil) - - nInterval := models.Interval{Name: testInterval.Name, Start: "34343", Timestamps: testTimestamps} - - _, err := addNewInterval(nInterval, myMock, mySchedulerMock) - if err != nil { - switch err.(type) { - case errorsSched.ErrInvalidTimeFormat: - // expected - default: - t.Fatalf("Expected errors.ErrInvalidTimeFormat") - } - } -} diff --git a/internal/support/scheduler/loader.go b/internal/support/scheduler/loader.go deleted file mode 100644 index 2b22d3ffa5..0000000000 --- a/internal/support/scheduler/loader.go +++ /dev/null @@ -1,322 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Inc. - * - * 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 scheduler - -import ( - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/config" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/interfaces" -) - -// Utility function for adding configured locally intervals and scheduled events -func LoadScheduler( - lc logger.LoggingClient, - dbClient interfaces.DBClient, - scClient interfaces.SchedulerQueueClient, - configuration *config.ConfigurationStruct) error { - - // ensure maps are clean - clearMaps() - - // ensure queue is empty - clearQueue() - - lc.Info("loading intervals, interval actions ...") - - // load data from support-scheduler database - err := loadSupportSchedulerDBInformation(lc, dbClient, scClient) - if err != nil { - return err - } - - // load config intervals - errLCI := loadConfigIntervals(lc, dbClient, scClient, configuration) - if errLCI != nil { - return errLCI - } - - // load config interval actions - errLCA := loadConfigIntervalActions(lc, dbClient, scClient, configuration) - if errLCA != nil { - return errLCA - } - - lc.Info("finished loading intervals, interval actions") - - return nil -} - -// Query support-scheduler scheduler client get intervals -func getSchedulerDBIntervals( - lc logger.LoggingClient, - dbClient interfaces.DBClient) ([]contract.Interval, error) { - - var err error - var intervals []contract.Interval - - intervals, err = dbClient.Intervals() - - if err != nil { - return intervals, err - } - - if intervals != nil { - lc.Debug("successfully queried support-scheduler intervals...") - for _, v := range intervals { - lc.Debug("found interval", "name", v.Name, "id", v.ID, "start", v.Start) - } - } - return intervals, nil -} - -// Query support-scheduler schedulerEvent client get scheduledEvents -func getSchedulerDBIntervalActions( - lc logger.LoggingClient, - dbClient interfaces.DBClient) ([]contract.IntervalAction, error) { - - var err error - var intervalActions []contract.IntervalAction - - intervalActions, err = dbClient.IntervalActions() - if err != nil { - return intervalActions, err - } - - // debug information only - if intervalActions != nil { - lc.Debug("successfully queried support-scheduler interval actions...") - for _, v := range intervalActions { - lc.Debug( - "found interval action", - "name", - v.Name, "id", - v.ID, - "interval", - v.Interval, - "target", - v.Target) - } - } - - return intervalActions, nil -} - -// Iterate over the received intervals add them to scheduler memory queue -func addReceivedIntervals( - intervals []contract.Interval, - lc logger.LoggingClient, - scClient interfaces.SchedulerQueueClient) error { - - for _, interval := range intervals { - err := scClient.AddIntervalToQueue(interval) - if err != nil { - lc.Info("problem adding support-scheduler interval name: %s - %s", interval.Name, err.Error()) - return err - } - lc.Info("added interval", "name", interval.Name, "id", interval.ID) - } - return nil -} - -// Iterate over the received interval action(s) -func addReceivedIntervalActions( - intervalActions []contract.IntervalAction, - lc logger.LoggingClient, - scClient interfaces.SchedulerQueueClient) error { - - for _, intervalAction := range intervalActions { - err := scClient.AddIntervalActionToQueue(intervalAction) - if err != nil { - lc.Info( - "problem adding support-scheduler interval action", - "name:", - intervalAction.Name, - "message", - err.Error()) - return err - } - lc.Info("added interval action", "name", intervalAction.Name, "id", intervalAction.ID) - } - return nil -} - -// Add interval to support-scheduler -func addIntervalToSchedulerDB( - interval contract.Interval, - lc logger.LoggingClient, - dbClient interfaces.DBClient) (string, error) { - - var err error - var id string - - id, err = dbClient.AddInterval(interval) - if err != nil { - return "", err - } - interval.ID = id - - lc.Info("added interval to the support-scheduler database", "name", interval.Name, "id", ID) - - return id, nil -} - -// Add interval event to support-scheduler -func addIntervalActionToSchedulerDB( - intervalAction contract.IntervalAction, - lc logger.LoggingClient, - dbClient interfaces.DBClient) (string, error) { - - var err error - var id string - - id, err = dbClient.AddIntervalAction(intervalAction) - if err != nil { - return "", err - } - lc.Info("added interval action to the support-scheduler", "name", intervalAction.Name, "id", id) - - return id, nil -} - -// Load intervals -func loadConfigIntervals( - lc logger.LoggingClient, - dbClient interfaces.DBClient, - scClient interfaces.SchedulerQueueClient, - configuration *config.ConfigurationStruct) error { - - intervals := configuration.Intervals - for i := range intervals { - interval := contract.Interval{ - ID: "", - Timestamps: contract.Timestamps{}, - Name: intervals[i].Name, - Start: intervals[i].Start, - End: intervals[i].End, - Frequency: intervals[i].Interval, - Cron: intervals[i].Cron, - RunOnce: intervals[i].RunOnce, - } - - // query scheduler service for interval in memory queue - _, errExistingSchedule := scClient.QueryIntervalByName(interval.Name) - - if errExistingSchedule != nil { - // add the interval support-scheduler - newIntervalID, errAddedInterval := addIntervalToSchedulerDB(interval, lc, dbClient) - if errAddedInterval != nil { - return errAddedInterval - } - - // add the support-scheduler scheduler.id - interval.ID = newIntervalID - - // add the interval to the scheduler - err := scClient.AddIntervalToQueue(interval) - - if err != nil { - return err - } - } else { - lc.Debug( - "did not add interval as it already exists in the scheduler database", "name", - interval.Name) - } - } - - return nil -} - -// Load interval actions if required -func loadConfigIntervalActions( - lc logger.LoggingClient, - dbClient interfaces.DBClient, - scClient interfaces.SchedulerQueueClient, - configuration *config.ConfigurationStruct) error { - - intervalActions := configuration.IntervalActions - - for ia := range intervalActions { - intervalAction := contract.IntervalAction{ - Name: intervalActions[ia].Name, - Interval: intervalActions[ia].Interval, - Parameters: intervalActions[ia].Parameters, - Target: intervalActions[ia].Target, - Path: intervalActions[ia].Path, - Port: intervalActions[ia].Port, - Protocol: intervalActions[ia].Protocol, - HTTPMethod: intervalActions[ia].Method, - Address: intervalActions[ia].Host, - } - - // query scheduler in memory queue and determine of intervalAction exists - _, err := scClient.QueryIntervalActionByName(intervalAction.Name) - - if err != nil { - - // add the interval action to support-scheduler database - newIntervalActionID, err := addIntervalActionToSchedulerDB(intervalAction, lc, dbClient) - if err != nil { - return err - } - - // add the support-scheduler version of the intervalAction.ID - intervalAction.ID = newIntervalActionID - // TODO: Do we care about the Created,Modified, or Origin fields? - - errAddIntervalAction := scClient.AddIntervalActionToQueue(intervalAction) - if errAddIntervalAction != nil { - return errAddIntervalAction - - } - } else { - lc.Debug( - "did not load interval action as it exists in the scheduler database" + - ":" + intervalAction.Name) - } - } - return nil -} - -// Query support-scheduler database information -func loadSupportSchedulerDBInformation( - lc logger.LoggingClient, - dbClient interfaces.DBClient, - scClient interfaces.SchedulerQueueClient) error { - - receivedIntervals, err := getSchedulerDBIntervals(lc, dbClient) - if err != nil { - return err - } - - err = addReceivedIntervals(receivedIntervals, lc, scClient) - if err != nil { - return err - } - - intervalActions, err := getSchedulerDBIntervalActions(lc, dbClient) - if err != nil { - return err - } - - err = addReceivedIntervalActions(intervalActions, lc, scClient) - if err != nil { - return err - } - - return nil -} diff --git a/internal/support/scheduler/main.go b/internal/support/scheduler/main.go index f00fbd6e23..27065d5dde 100644 --- a/internal/support/scheduler/main.go +++ b/internal/support/scheduler/main.go @@ -21,7 +21,6 @@ import ( "github.com/edgexfoundry/edgex-go" "github.com/edgexfoundry/edgex-go/internal" - "github.com/edgexfoundry/edgex-go/internal/pkg/bootstrap/handlers/database" "github.com/edgexfoundry/edgex-go/internal/pkg/telemetry" v2Handlers "github.com/edgexfoundry/edgex-go/internal/pkg/v2/bootstrap/handlers" "github.com/edgexfoundry/edgex-go/internal/support/scheduler/config" @@ -39,7 +38,7 @@ import ( "github.com/gorilla/mux" ) -func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router, readyStream chan<- bool) { +func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router) { startupTimer := startup.NewStartUpTimer(clients.SupportSchedulerServiceKey) // All common command-line flags have been moved to DefaultCommonFlags. Service specific flags can be add here, @@ -72,12 +71,10 @@ func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router, re dic, true, []interfaces.BootstrapHandler{ - database.NewDatabase(httpServer, configuration).BootstrapHandler, v2Handlers.NewDatabase(httpServer, configuration, v2SchedulerContainer.DBClientInterfaceName).BootstrapHandler, // add v2 db client bootstrap handler NewBootstrap(router).BootstrapHandler, telemetry.BootstrapHandler, httpServer.BootstrapHandler, handlers.NewStartMessage(clients.SupportSchedulerServiceKey, edgex.Version).BootstrapHandler, - handlers.NewReady(httpServer, readyStream).BootstrapHandler, }) } diff --git a/internal/support/scheduler/operators/interval/add.go b/internal/support/scheduler/operators/interval/add.go deleted file mode 100644 index ec6b70ac34..0000000000 --- a/internal/support/scheduler/operators/interval/add.go +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 interval - -import ( - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/errors" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type AddExecutor interface { - Execute() (string, error) -} - -type intervalAdd struct { - database IntervalWriter - scClient SchedulerQueueWriter - interval contract.Interval -} - -// This method adds the provided Addressable to the database. -func (op intervalAdd) Execute() (id string, err error) { - name := op.interval.Name - - // Check if the name is unique - ret, err := op.database.IntervalByName(name) - if err == nil && ret.Name == name { - return "", errors.NewErrIntervalNameInUse(name) - } - // Add the new interval to the database - ID, err := op.database.AddInterval(op.interval) - if err != nil { - return "", err - } - - // Push the new interval into scheduler queue - op.interval.ID = ID - err = op.scClient.AddIntervalToQueue(op.interval) - if err != nil { - return ID, err - } - return ID, nil -} - -// This factory method returns an executor used to add an addressable. -func NewAddExecutor(db IntervalWriter, scClient SchedulerQueueWriter, interval contract.Interval) AddExecutor { - return intervalAdd{ - database: db, - scClient: scClient, - interval: interval, - } -} diff --git a/internal/support/scheduler/operators/interval/add_test.go b/internal/support/scheduler/operators/interval/add_test.go deleted file mode 100644 index 916d2ec07c..0000000000 --- a/internal/support/scheduler/operators/interval/add_test.go +++ /dev/null @@ -1,161 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 interval - -import ( - "reflect" - "testing" - - intervalErrors "github.com/edgexfoundry/edgex-go/internal/support/scheduler/errors" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/operators/interval/mocks" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -var ValidInterval = SuccessfulDatabaseResult[0] -var OtherValidInterval = SuccessfulDatabaseResult[1] -var InvalidStartInterval = SuccessfulDatabaseResult[2] -var InvalidEndInterval = SuccessfulDatabaseResult[3] -var InvalidFreqInterval = SuccessfulDatabaseResult[4] - -func TestAddExecutor(t *testing.T) { - - tests := []struct { - name string - mockDb IntervalWriter - scClient SchedulerQueueWriter - interval contract.Interval - expectedResult string - expectedError bool - expectedErrorVal error - }{ - - { - name: "Successful database call", - mockDb: createAddMockIntervalSuccess(), - scClient: createAddMockIntervalSCSuccess(), - interval: ValidInterval, - expectedResult: ValidInterval.ID, - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "Error Interval In Use", - mockDb: createAddMockIntervalInUse(), - scClient: createAddMockIntervalSCSuccess(), - interval: ValidInterval, - expectedResult: "", - expectedError: true, - expectedErrorVal: intervalErrors.NewErrIntervalNameInUse(SuccessfulDatabaseResult[0].Name), - }, - { - name: "Error AddInterval", - mockDb: createAddMockIntervalError(), - scClient: createAddMockIntervalSCSuccess(), - interval: ValidInterval, - expectedResult: "", - expectedError: true, - expectedErrorVal: Error, - }, - { - name: "Error AddToQueue", - mockDb: createAddMockIntervalSuccess(), - scClient: createAddMockIntervalSCError(), - interval: ValidInterval, - expectedResult: ValidInterval.ID, - expectedError: true, - expectedErrorVal: Error, - }, - } - - for _, test := range tests { - t.Run(test.name, func(tt *testing.T) { - op := NewAddExecutor(test.mockDb, test.scClient, test.interval) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -func createAddMockIntervalSuccess() IntervalWriter { - dbMock := mocks.IntervalWriter{} - dbMock.On("IntervalByName", Name).Return(OtherValidInterval, nil) - dbMock.On("AddInterval", ValidInterval).Return(Id, nil) - return &dbMock -} - -func createAddMockIntervalInUse() IntervalWriter { - dbMock := mocks.IntervalWriter{} - dbMock.On("IntervalByName", Name).Return(ValidInterval, nil) - dbMock.On("AddInterval", ValidInterval).Return(Id, nil) - return &dbMock -} - -func createAddMockIntervalInvalidFreq() IntervalWriter { - dbMock := mocks.IntervalWriter{} - dbMock.On("IntervalByName", InvalidFreqInterval.Name).Return(OtherValidInterval, nil) - dbMock.On("AddInterval", ValidInterval).Return(Id, nil) - return &dbMock -} - -func createAddMockIntervalError() IntervalWriter { - dbMock := mocks.IntervalWriter{} - dbMock.On("IntervalByName", ValidInterval.Name).Return(OtherValidInterval, nil) - dbMock.On("AddInterval", ValidInterval).Return("", Error) - return &dbMock -} - -func createAddMockInvalidStart() IntervalWriter { - dbMock := mocks.IntervalWriter{} - dbMock.On("IntervalByName", InvalidStartInterval.Name).Return(ValidInterval, nil) - dbMock.On("AddInterval", ValidInterval).Return(Id, nil) - return &dbMock -} - -func createAddMockInvalidEnd() IntervalWriter { - dbMock := mocks.IntervalWriter{} - dbMock.On("IntervalByName", InvalidEndInterval.Name).Return(ValidInterval, nil) - dbMock.On("AddInterval", ValidInterval).Return(Id, nil) - return &dbMock -} - -func createAddMockIntervalSCSuccess() SchedulerQueueWriter { - dbMock := mocks.SchedulerQueueWriter{} - dbMock.On("AddIntervalToQueue", ValidInterval).Return(nil) - return &dbMock -} - -func createAddMockIntervalSCError() SchedulerQueueWriter { - dbMock := mocks.SchedulerQueueWriter{} - dbMock.On("AddIntervalToQueue", ValidInterval).Return(Error) - return &dbMock -} diff --git a/internal/support/scheduler/operators/interval/db.go b/internal/support/scheduler/operators/interval/db.go deleted file mode 100644 index 5f32c21890..0000000000 --- a/internal/support/scheduler/operators/interval/db.go +++ /dev/null @@ -1,73 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 interval - -import contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// IntervalLoader provides functionality for obtaining Interval. -type IntervalLoader interface { - Intervals() ([]contract.Interval, error) - IntervalsWithLimit(limit int) ([]contract.Interval, error) - IntervalById(id string) (contract.Interval, error) - IntervalByName(name string) (contract.Interval, error) -} - -// IntervalDeleter deletes interval. -type IntervalDeleter interface { - DeleteIntervalById(id string) error - ScrubAllIntervals() (int, error) - IntervalLoader - IntervalActionLoader -} - -// IntervalWriter adds interval. -type IntervalWriter interface { - AddInterval(interval contract.Interval) (string, error) - IntervalLoader -} - -// IntervalUpdater updates interval. -type IntervalUpdater interface { - UpdateInterval(interval contract.Interval) error - IntervalLoader - IntervalActionLoader -} - -// SchedulerQueueLoader provides functionality for obtaining Interval from SchedulerQueue -type SchedulerQueueLoader interface { - QueryIntervalByID(intervalId string) (contract.Interval, error) - QueryIntervalByName(intervalName string) (contract.Interval, error) -} - -// SchedulerQueueDeleter deletes interval from SchedulerQueue -type SchedulerQueueDeleter interface { - RemoveIntervalInQueue(intervalId string) error - SchedulerQueueLoader -} - -// SchedulerQueueWriter adds interval in SchedulerQueue -type SchedulerQueueWriter interface { - AddIntervalToQueue(interval contract.Interval) error - SchedulerQueueLoader -} - -// SchedulerQueueUpdater update interval in SchedulerQueue -type SchedulerQueueUpdater interface { - UpdateIntervalInQueue(interval contract.Interval) error - SchedulerQueueLoader -} - -type IntervalActionLoader interface { - IntervalActionsByIntervalName(name string) ([]contract.IntervalAction, error) -} diff --git a/internal/support/scheduler/operators/interval/delete.go b/internal/support/scheduler/operators/interval/delete.go deleted file mode 100644 index dd2367cd4a..0000000000 --- a/internal/support/scheduler/operators/interval/delete.go +++ /dev/null @@ -1,158 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 interval - -import ( - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/errors" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -// DeleteExecutor handles the deletion of a interval. -// Returns ErrIntervalNotFound if an interval could not be found with a matching ID -type DeleteExecutor interface { - Execute() error -} - -type ScrubExecutor interface { - Execute() (int, error) -} - -type deleteIntervalByID struct { - intervalLoader IntervalLoader - intervalActionLoader IntervalActionLoader - intervalDeleter IntervalDeleter - sqDeleter SchedulerQueueDeleter - did string -} - -type deleteIntervalByName struct { - intervalLoader IntervalLoader - intervalActionLoader IntervalActionLoader - intervalDeleter IntervalDeleter - sqDeleter SchedulerQueueDeleter - dname string -} - -type scrubIntervals struct { - db IntervalDeleter -} - -// Execute() deletes the interval by ID. -func (dibi deleteIntervalByID) Execute() error { - // Check in memory. - inMemory, err := dibi.sqDeleter.QueryIntervalByID(dibi.did) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrIntervalNotFound(dibi.did) - } - return err - } - - return deleteInterval(inMemory, dibi.intervalDeleter, dibi.sqDeleter) -} - -// Execute() deletes the interval by Name. -func (dibn deleteIntervalByName) Execute() error { - // Check in memory. - inMemory, err := dibn.sqDeleter.QueryIntervalByName(dibn.dname) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrIntervalNotFound(dibn.dname) - } - return err - } - - return deleteInterval(inMemory, dibn.intervalDeleter, dibn.sqDeleter) -} - -// deleteInterval first checks the Interval to determine that it is not in use before deleting -// from both memory and database. Note that a failure in this function may result in the system -// ending up in an undesirable state, and "rollbacks" are not handled here. For example, if we -// first remove the Interval from memory, then encounter failure while trying to delete from the -// database, we will end up with GET /interval API calls still responding with the "deleted" Interval. -func deleteInterval( - interval contract.Interval, - intervalDeleter IntervalDeleter, - sqDeleter SchedulerQueueDeleter) error { - - // Check if interval is in use. Get all IntervalActions that are associated with this interval. - allIntervalActions, err := intervalDeleter.IntervalActionsByIntervalName(interval.Name) - if err != nil { - return err - } - - if len(allIntervalActions) != 0 { - return errors.NewErrIntervalNameInUse(interval.Name) - } - - // Remove interval in memory - err = sqDeleter.RemoveIntervalInQueue(interval.ID) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrIntervalNotFound(interval.ID) - } - return err - } - - // Delete the interval - if err = intervalDeleter.DeleteIntervalById(interval.ID); err != nil { - return err - } - - return nil -} - -func (si scrubIntervals) Execute() (int, error) { - count, err := si.db.ScrubAllIntervals() - if err != nil { - return 0, err - } - return count, nil -} - -// NewDeleteByIDExecutor creates a new DeleteExecutor which deletes an interval based on id. -func NewDeleteByIDExecutor( - intervalDeleter IntervalDeleter, - sqDeleter SchedulerQueueDeleter, - did string) DeleteExecutor { - - return deleteIntervalByID{ - intervalDeleter: intervalDeleter, - sqDeleter: sqDeleter, - did: did, - } -} - -// NewDeleteByNameExecutor creates a new DeleteExecutor which deletes an interval based on name. -func NewDeleteByNameExecutor( - intervalDeleter IntervalDeleter, - sqDeleter SchedulerQueueDeleter, - dname string) DeleteExecutor { - - return deleteIntervalByName{ - intervalDeleter: intervalDeleter, - sqDeleter: sqDeleter, - dname: dname, - } -} - -// NewDeleteByScrubExecutor creates a new DeleteExecutor which scrubs intervals. -func NewScrubExecutor(db IntervalDeleter) ScrubExecutor { - return scrubIntervals{ - db: db, - } -} diff --git a/internal/support/scheduler/operators/interval/delete_test.go b/internal/support/scheduler/operators/interval/delete_test.go deleted file mode 100644 index 07731a75aa..0000000000 --- a/internal/support/scheduler/operators/interval/delete_test.go +++ /dev/null @@ -1,366 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 interval - -import ( - "reflect" - "testing" - - intervalErrors "github.com/edgexfoundry/edgex-go/internal/support/scheduler/errors" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/operators/interval/mocks" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -func createMockIdDBSuccessDel() IntervalDeleter { - dbMock := mocks.IntervalDeleter{} - dbMock.On("DeleteIntervalById", Id).Return(nil) - dbMock.On("IntervalById", Id).Return(contract.Interval{}, nil) - dbMock.On("IntervalActionsByIntervalName", Name).Return([]contract.IntervalAction{}, nil) - return &dbMock -} - -func createMockIdDBNotFoundErr() IntervalDeleter { - dbMock := mocks.IntervalDeleter{} - dbMock.On("DeleteIntervalById", Id).Return(ErrorNotFound) - dbMock.On("IntervalById", Id).Return(contract.Interval{}, nil) - dbMock.On("IntervalActionsByIntervalName", Name).Return([]contract.IntervalAction{}, nil) - return &dbMock -} - -func createMockIdDBErr() IntervalDeleter { - dbMock := mocks.IntervalDeleter{} - dbMock.On("DeleteIntervalById", Id).Return(Error) - dbMock.On("IntervalById", Id).Return(contract.Interval{}, nil) - dbMock.On("IntervalActionsByIntervalName", Name).Return([]contract.IntervalAction{}, nil) - return &dbMock -} - -func createMockIdSCSuccessDel() SchedulerQueueDeleter { - dbMock := mocks.SchedulerQueueDeleter{} - dbMock.On("QueryIntervalByID", Id).Return(SuccessfulDatabaseResult[0], nil) - dbMock.On("RemoveIntervalInQueue", Id).Return(nil) - dbMock.On("IntervalById", Id).Return(contract.Interval{}, nil) - dbMock.On("IntervalActionsByIntervalName", Name).Return([]contract.IntervalAction{}, nil) - return &dbMock -} - -func createMockIdSCNotFoundErr() SchedulerQueueDeleter { - dbMock := mocks.SchedulerQueueDeleter{} - dbMock.On("QueryIntervalByID", Id).Return(contract.Interval{}, ErrorNotFound) - dbMock.On("RemoveIntervalInQueue", Id).Return(nil) - dbMock.On("IntervalById", Id).Return(contract.Interval{}, nil) - dbMock.On("IntervalActionsByIntervalName", Name).Return([]contract.IntervalAction{}, nil) - return &dbMock -} - -func createMockIdSCNotFoundQueueErr() SchedulerQueueDeleter { - dbMock := mocks.SchedulerQueueDeleter{} - dbMock.On("QueryIntervalByID", Id).Return(SuccessfulDatabaseResult[0], nil) - dbMock.On("RemoveIntervalInQueue", Id).Return(ErrorNotFound) - dbMock.On("IntervalById", Id).Return(contract.Interval{}, nil) - dbMock.On("IntervalActionsByIntervalName", Name).Return([]contract.IntervalAction{}, nil) - return &dbMock -} - -func TestIntervalById(t *testing.T) { - tests := []struct { - name string - idMock IntervalDeleter - sqDeleter SchedulerQueueDeleter - expectError bool - expectedErrorType error - }{ - { - name: "Successful Delete", - idMock: createMockIdDBSuccessDel(), - sqDeleter: createMockIdSCSuccessDel(), - expectError: false, - expectedErrorType: nil, - }, - { - name: "Interval not found", - idMock: createMockIdDBNotFoundErr(), - sqDeleter: createMockIdSCSuccessDel(), - expectError: true, - expectedErrorType: Error, // The error being bubbled up is overly generic. - }, - { - name: "Delete error", - idMock: createMockIdDBErr(), - sqDeleter: createMockIdSCSuccessDel(), - expectError: true, - expectedErrorType: Error, - }, - { - name: "Error with getting interval", - idMock: createMockIdDBSuccessDel(), - sqDeleter: createMockIdSCNotFoundErr(), - expectError: true, - expectedErrorType: intervalErrors.ErrIntervalNotFound{}, - }, - { - name: "Error with removing interval in queue", - idMock: createMockIdDBSuccessDel(), - sqDeleter: createMockIdSCNotFoundQueueErr(), - expectError: true, - expectedErrorType: intervalErrors.ErrIntervalNotFound{}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewDeleteByIDExecutor(test.idMock, test.sqDeleter, Id) - err := op.Execute() - - if test.expectError && err == nil { - t.Error("We expected an error but did not get one") - return - } - - if !test.expectError && err != nil { - t.Errorf("We do not expected an error but got one. %s", err.Error()) - return - } - - if test.expectError { - eet := reflect.TypeOf(test.expectedErrorType) - aet := reflect.TypeOf(err) - if !aet.AssignableTo(eet) { - t.Errorf("Expected error of type %v, but got an error of type %v", eet, aet) - } - } - return - }) - } -} - -func createMockNameDBSuccessDel() IntervalDeleter { - dbMock := mocks.IntervalDeleter{} - dbMock.On("DeleteIntervalById", Id).Return(nil) - dbMock.On("IntervalByName", Name).Return(SuccessfulDatabaseResult[0], nil) - dbMock.On("IntervalActionsByIntervalName", Name).Return([]contract.IntervalAction{}, nil) - return &dbMock -} - -func createMockNameSCSuccessDel() SchedulerQueueDeleter { - dbMock := mocks.SchedulerQueueDeleter{} - dbMock.On("QueryIntervalByName", Name).Return(SuccessfulDatabaseResult[0], nil) - dbMock.On("RemoveIntervalInQueue", Id).Return(nil) - return &dbMock -} - -func createMockNameDBNotFound() IntervalDeleter { - dbMock := mocks.IntervalDeleter{} - dbMock.On("DeleteIntervalById", Id).Return(ErrorNotFound) - dbMock.On("IntervalByName", Name).Return(contract.Interval{}, ErrorNotFound) - dbMock.On("IntervalActionsByIntervalName", Name).Return([]contract.IntervalAction{}, ErrorNotFound) - return &dbMock -} - -func createMockNameIntervalByNameErr() IntervalDeleter { - dbMock := mocks.IntervalDeleter{} - dbMock.On("DeleteIntervalById", Id).Return(Error) - dbMock.On("IntervalByName", Name).Return(contract.Interval{}, Error) - dbMock.On("IntervalActionsByIntervalName", Name).Return([]contract.IntervalAction{}, Error) - return &dbMock -} - -func createMockNameIntervalStillInUseErr() IntervalDeleter { - dbMock := mocks.IntervalDeleter{} - dbMock.On("DeleteIntervalById", Id).Return(Error) - dbMock.On("IntervalByName", Name).Return(SuccessfulDatabaseResult[0], Error) - dbMock.On("IntervalActionsByIntervalName", Name).Return(SuccessfulIntervalActionResult, Error) - return &dbMock -} - -func createMockNameDeleteErr() IntervalDeleter { - dbMock := mocks.IntervalDeleter{} - dbMock.On("DeleteIntervalById", Id).Return(Error) - dbMock.On("IntervalByName", Name).Return(SuccessfulDatabaseResult[0], Error) - dbMock.On("IntervalActionsByIntervalName", Name).Return([]contract.IntervalAction{}, Error) - return &dbMock -} - -func createMockNameRemoveIntervalInQueueErr() SchedulerQueueDeleter { - dbMock := mocks.SchedulerQueueDeleter{} - dbMock.On("QueryIntervalByName", Name).Return(SuccessfulDatabaseResult[0], nil) - dbMock.On("RemoveIntervalInQueue", Id).Return(ErrorNotFound) - return &dbMock -} - -func createMockNameIntervalActionsByIntervalNameErr() IntervalDeleter { - dbMock := mocks.IntervalDeleter{} - dbMock.On("DeleteIntervalById", Id).Return(nil) - dbMock.On("IntervalByName", Name).Return(SuccessfulDatabaseResult[0], nil) - dbMock.On("IntervalActionsByIntervalName", Name).Return([]contract.IntervalAction{}, Error) - return &dbMock -} - -func createMockNameQueryIntervalByNameErr() SchedulerQueueDeleter { - dbMock := mocks.SchedulerQueueDeleter{} - dbMock.On("QueryIntervalByName", Name).Return(contract.Interval{}, ErrorNotFound) - dbMock.On("RemoveIntervalInQueue", Id).Return(nil) - return &dbMock -} - -func TestIntervalByName(t *testing.T) { - tests := []struct { - name string - idMock IntervalDeleter - sqDeleter SchedulerQueueDeleter - expectError bool - expectedErrorType error - }{ - { - name: "Successful Delete", - idMock: createMockNameDBSuccessDel(), - sqDeleter: createMockNameSCSuccessDel(), - expectError: false, - expectedErrorType: nil, - }, - { - name: "Interval not found", - idMock: createMockNameDBNotFound(), - sqDeleter: createMockNameSCSuccessDel(), - expectError: true, - expectedErrorType: Error, // The error being bubbled up is overly generic. - }, - { - name: "IntervalByName error", - idMock: createMockNameIntervalByNameErr(), - sqDeleter: createMockNameSCSuccessDel(), - expectError: true, - expectedErrorType: Error, - }, - { - name: "IntervalStillInUse error", - idMock: createMockNameIntervalStillInUseErr(), - sqDeleter: createMockNameSCSuccessDel(), - expectError: true, - expectedErrorType: Error, - }, - { - name: "Delete error", - idMock: createMockNameDeleteErr(), - sqDeleter: createMockNameSCSuccessDel(), - expectError: true, - expectedErrorType: Error, - }, - { - name: "Delete RemoveIntervalInQueue error", - idMock: createMockNameDBSuccessDel(), - sqDeleter: createMockNameRemoveIntervalInQueueErr(), - expectError: true, - expectedErrorType: intervalErrors.ErrIntervalNotFound{}, - }, - { - name: "Delete IntervalActionsByIntervalName error", - idMock: createMockNameIntervalActionsByIntervalNameErr(), - sqDeleter: createMockNameSCSuccessDel(), - expectError: true, - expectedErrorType: Error, - }, - { - name: "Error QueryIntervalByName", - idMock: createMockNameDBSuccessDel(), - sqDeleter: createMockNameQueryIntervalByNameErr(), - expectError: true, - expectedErrorType: intervalErrors.ErrIntervalNotFound{}, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewDeleteByNameExecutor(test.idMock, test.sqDeleter, Name) - err := op.Execute() - - if test.expectError && err == nil { - t.Error("We expected an error but did not get one") - return - } - - if !test.expectError && err != nil { - t.Errorf("We do not expected an error but got one. %s", err.Error()) - return - } - - if test.expectError { - eet := reflect.TypeOf(test.expectedErrorType) - aet := reflect.TypeOf(err) - if !aet.AssignableTo(eet) { - t.Errorf("Expected error of type %v, but got an error of type %v", eet, aet) - } - } - return - }) - } -} - -func createMockScrubDBSuccessDel() IntervalDeleter { - dbMock := mocks.IntervalDeleter{} - dbMock.On("ScrubAllIntervals").Return(1, nil) - return &dbMock -} - -func createMockScrubDBSuccessErr() IntervalDeleter { - dbMock := mocks.IntervalDeleter{} - dbMock.On("ScrubAllIntervals").Return(0, Error) - return &dbMock -} - -func TestScrubIntervals_Execute(t *testing.T) { - tests := []struct { - name string - database IntervalDeleter - expectError bool - expectedErrorType error - }{ - { - name: "Successful Delete", - database: createMockScrubDBSuccessDel(), - expectError: false, - expectedErrorType: nil, - }, - { - name: "Delete error", - database: createMockScrubDBSuccessErr(), - expectError: true, - expectedErrorType: Error, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewScrubExecutor(test.database) - _, err := op.Execute() - - if test.expectError && err == nil { - t.Error("We expected an error but did not get one") - } - - if !test.expectError && err != nil { - t.Errorf("We do not expected an error but got one. %s", err.Error()) - } - - if test.expectError { - eet := reflect.TypeOf(test.expectedErrorType) - aet := reflect.TypeOf(err) - if !aet.AssignableTo(eet) { - t.Errorf("Expected error of type %v, but got an error of type %v", eet, aet) - } - } - return - }) - } -} diff --git a/internal/support/scheduler/operators/interval/get.go b/internal/support/scheduler/operators/interval/get.go deleted file mode 100644 index 123107d731..0000000000 --- a/internal/support/scheduler/operators/interval/get.go +++ /dev/null @@ -1,103 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 interval - -import ( - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/errors" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type IdExecutor interface { - Execute() (contract.Interval, error) -} - -type CollectionExecutor interface { - Execute() ([]contract.Interval, error) -} - -type intervalLoadAll struct { - database IntervalLoader - limit int -} - -type intervalLoadById struct { - database IntervalLoader - id string -} - -type intervalLoadByName struct { - database IntervalLoader - name string -} - -func (op intervalLoadAll) Execute() ([]contract.Interval, error) { - var err error - var intervals []contract.Interval - - if op.limit <= 0 { - intervals, err = op.database.Intervals() - } else { - intervals, err = op.database.IntervalsWithLimit(op.limit) - } - - if err != nil { - return nil, err - } - - return intervals, err -} -func (op intervalLoadById) Execute() (contract.Interval, error) { - res, err := op.database.IntervalById(op.id) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrIntervalNotFound(op.id) - } - return res, err - } - return res, nil -} - -func (op intervalLoadByName) Execute() (contract.Interval, error) { - res, err := op.database.IntervalByName(op.name) - if err != nil { - if err == db.ErrNotFound { - err = errors.NewErrIntervalNotFound(op.name) - } - return res, err - } - return res, nil -} - -func NewAllExecutor(db IntervalLoader, limit int) CollectionExecutor { - return intervalLoadAll{ - database: db, - limit: limit, - } -} - -func NewIdExecutor(db IntervalLoader, id string) IdExecutor { - return intervalLoadById{ - database: db, - id: id, - } -} - -func NewNameExecutor(db IntervalLoader, name string) IdExecutor { - return intervalLoadByName{ - database: db, - name: name, - } -} diff --git a/internal/support/scheduler/operators/interval/get_test.go b/internal/support/scheduler/operators/interval/get_test.go deleted file mode 100644 index 8a94ad944f..0000000000 --- a/internal/support/scheduler/operators/interval/get_test.go +++ /dev/null @@ -1,260 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 interval - -import ( - "errors" - "reflect" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/operators/interval/mocks" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -var Id = "83cb038b-5a94-4707-985d-13effec62de2" -var Name = "hourly" -var OtherName = "other" -var Error = errors.New("test error") -var ErrorNotFound = db.ErrNotFound -var TestLimit = 5 -var SuccessfulDatabaseResult = []contract.Interval{ - { - ID: Id, - Name: "hourly", - Start: "20160101T000000", - End: "", - Frequency: "PT1H", - }, - { - ID: Id, - Name: "weekly", - Start: "20160101T000000", - End: "", - Frequency: "PT1H", - }, - { - ID: Id, - Name: "weekly2", - Start: "invalid", - End: "", - Frequency: "PT1H", - }, - { - ID: Id, - Name: "weekly3", - Start: "20160101T000000", - End: "invalid", - Frequency: "PT1H", - }, - { - ID: Id, - Name: "weekly4", - Start: "20160101T000000", - End: "", - Frequency: "PT1H!", - }, -} - -var SuccessfulIntervalActionResult = []contract.IntervalAction{ - { - ID: Id, - Name: "scrub pushed records", - Interval: "hourly", - Parameters: "", - }, -} - -func TestAllExecutor(t *testing.T) { - tests := []struct { - name string - mockDb IntervalLoader - expectedResult []contract.Interval - expectedError bool - }{ - { - name: "Successful database call", - mockDb: createMockIntervalsSuccess(), - expectedResult: SuccessfulDatabaseResult, - expectedError: false, - }, - { - name: "Unexpected error", - mockDb: createMockIntervalsFail(), - expectedResult: nil, - expectedError: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewAllExecutor(test.mockDb, 0) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewAllExecutor(test.mockDb, TestLimit) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func TestIdExecutor(t *testing.T) { - tests := []struct { - name string - mockDb IntervalLoader - expectedResult contract.Interval - expectedError bool - }{ - { - name: "Successful database call", - mockDb: createMockIntervalLoaderStringArg("IntervalById", nil, SuccessfulDatabaseResult[0], Id), - expectedResult: SuccessfulDatabaseResult[0], - expectedError: false, - }, - { - name: "Unexpected error", - mockDb: createMockIntervalLoaderStringArg("IntervalById", Error, contract.Interval{}, Id), - expectedResult: contract.Interval{}, - expectedError: true, - }, - { - name: "Interval not found error", - mockDb: createMockIntervalLoaderStringArg("IntervalById", ErrorNotFound, contract.Interval{}, Id), - expectedResult: contract.Interval{}, - expectedError: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewIdExecutor(test.mockDb, Id) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func createMockIntervalsSuccess() IntervalLoader { - dbMock := mocks.IntervalLoader{} - dbMock.On("Intervals").Return(SuccessfulDatabaseResult, nil) - dbMock.On("IntervalsWithLimit", TestLimit).Return(SuccessfulDatabaseResult, nil) - return &dbMock -} - -func createMockIntervalsFail() IntervalLoader { - dbMock := mocks.IntervalLoader{} - dbMock.On("Intervals").Return([]contract.Interval{}, Error) - dbMock.On("IntervalsWithLimit", TestLimit).Return([]contract.Interval{}, Error) - return &dbMock -} - -func createMockIntervalLoaderStringArg(methodName string, err error, ret interface{}, arg string) IntervalLoader { - dbMock := mocks.IntervalLoader{} - dbMock.On(methodName, arg).Return(ret, err) - return &dbMock -} - -func TestNameExecutor(t *testing.T) { - tests := []struct { - name string - mockDb IntervalLoader - expectedResult contract.Interval - expectedError bool - }{ - { - name: "Successful database call", - mockDb: createMockIntervalLoaderStringArg("IntervalByName", nil, SuccessfulDatabaseResult[0], Name), - expectedResult: SuccessfulDatabaseResult[0], - expectedError: false, - }, - { - name: "Unexpected error", - mockDb: createMockIntervalLoaderStringArg("IntervalByName", Error, contract.Interval{}, Name), - expectedResult: contract.Interval{}, - expectedError: true, - }, - { - name: "Interval not found error", - mockDb: createMockIntervalLoaderStringArg("IntervalByName", ErrorNotFound, contract.Interval{}, Name), - expectedResult: contract.Interval{}, - expectedError: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewNameExecutor(test.mockDb, Name) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} diff --git a/internal/support/scheduler/operators/interval/mocks/IntervalActionLoader.go b/internal/support/scheduler/operators/interval/mocks/IntervalActionLoader.go deleted file mode 100644 index 9997ffd008..0000000000 --- a/internal/support/scheduler/operators/interval/mocks/IntervalActionLoader.go +++ /dev/null @@ -1,34 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// IntervalActionLoader is an autogenerated mock type for the IntervalActionLoader type -type IntervalActionLoader struct { - mock.Mock -} - -// IntervalActionsByIntervalName provides a mock function with given fields: name -func (_m *IntervalActionLoader) IntervalActionsByIntervalName(name string) ([]models.IntervalAction, error) { - ret := _m.Called(name) - - var r0 []models.IntervalAction - if rf, ok := ret.Get(0).(func(string) []models.IntervalAction); ok { - r0 = rf(name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.IntervalAction) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/support/scheduler/operators/interval/mocks/IntervalDeleter.go b/internal/support/scheduler/operators/interval/mocks/IntervalDeleter.go deleted file mode 100644 index e6ea456e1d..0000000000 --- a/internal/support/scheduler/operators/interval/mocks/IntervalDeleter.go +++ /dev/null @@ -1,157 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// IntervalDeleter is an autogenerated mock type for the IntervalDeleter type -type IntervalDeleter struct { - mock.Mock -} - -// DeleteIntervalById provides a mock function with given fields: id -func (_m *IntervalDeleter) DeleteIntervalById(id string) error { - ret := _m.Called(id) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(id) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// IntervalActionsByIntervalName provides a mock function with given fields: name -func (_m *IntervalDeleter) IntervalActionsByIntervalName(name string) ([]models.IntervalAction, error) { - ret := _m.Called(name) - - var r0 []models.IntervalAction - if rf, ok := ret.Get(0).(func(string) []models.IntervalAction); ok { - r0 = rf(name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.IntervalAction) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalById provides a mock function with given fields: id -func (_m *IntervalDeleter) IntervalById(id string) (models.Interval, error) { - ret := _m.Called(id) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalByName provides a mock function with given fields: name -func (_m *IntervalDeleter) IntervalByName(name string) (models.Interval, error) { - ret := _m.Called(name) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(name) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Intervals provides a mock function with given fields: -func (_m *IntervalDeleter) Intervals() ([]models.Interval, error) { - ret := _m.Called() - - var r0 []models.Interval - if rf, ok := ret.Get(0).(func() []models.Interval); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Interval) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalsWithLimit provides a mock function with given fields: limit -func (_m *IntervalDeleter) IntervalsWithLimit(limit int) ([]models.Interval, error) { - ret := _m.Called(limit) - - var r0 []models.Interval - if rf, ok := ret.Get(0).(func(int) []models.Interval); ok { - r0 = rf(limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Interval) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ScrubAllIntervals provides a mock function with given fields: -func (_m *IntervalDeleter) ScrubAllIntervals() (int, error) { - ret := _m.Called() - - var r0 int - if rf, ok := ret.Get(0).(func() int); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int) - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/support/scheduler/operators/interval/mocks/IntervalLoader.go b/internal/support/scheduler/operators/interval/mocks/IntervalLoader.go deleted file mode 100644 index d5279cd2f9..0000000000 --- a/internal/support/scheduler/operators/interval/mocks/IntervalLoader.go +++ /dev/null @@ -1,99 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// IntervalLoader is an autogenerated mock type for the IntervalLoader type -type IntervalLoader struct { - mock.Mock -} - -// IntervalById provides a mock function with given fields: id -func (_m *IntervalLoader) IntervalById(id string) (models.Interval, error) { - ret := _m.Called(id) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalByName provides a mock function with given fields: name -func (_m *IntervalLoader) IntervalByName(name string) (models.Interval, error) { - ret := _m.Called(name) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(name) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Intervals provides a mock function with given fields: -func (_m *IntervalLoader) Intervals() ([]models.Interval, error) { - ret := _m.Called() - - var r0 []models.Interval - if rf, ok := ret.Get(0).(func() []models.Interval); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Interval) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalsWithLimit provides a mock function with given fields: limit -func (_m *IntervalLoader) IntervalsWithLimit(limit int) ([]models.Interval, error) { - ret := _m.Called(limit) - - var r0 []models.Interval - if rf, ok := ret.Get(0).(func(int) []models.Interval); ok { - r0 = rf(limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Interval) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/support/scheduler/operators/interval/mocks/IntervalUpdater.go b/internal/support/scheduler/operators/interval/mocks/IntervalUpdater.go deleted file mode 100644 index acaa975334..0000000000 --- a/internal/support/scheduler/operators/interval/mocks/IntervalUpdater.go +++ /dev/null @@ -1,136 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// IntervalUpdater is an autogenerated mock type for the IntervalUpdater type -type IntervalUpdater struct { - mock.Mock -} - -// IntervalActionsByIntervalName provides a mock function with given fields: name -func (_m *IntervalUpdater) IntervalActionsByIntervalName(name string) ([]models.IntervalAction, error) { - ret := _m.Called(name) - - var r0 []models.IntervalAction - if rf, ok := ret.Get(0).(func(string) []models.IntervalAction); ok { - r0 = rf(name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.IntervalAction) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalById provides a mock function with given fields: id -func (_m *IntervalUpdater) IntervalById(id string) (models.Interval, error) { - ret := _m.Called(id) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalByName provides a mock function with given fields: name -func (_m *IntervalUpdater) IntervalByName(name string) (models.Interval, error) { - ret := _m.Called(name) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(name) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Intervals provides a mock function with given fields: -func (_m *IntervalUpdater) Intervals() ([]models.Interval, error) { - ret := _m.Called() - - var r0 []models.Interval - if rf, ok := ret.Get(0).(func() []models.Interval); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Interval) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalsWithLimit provides a mock function with given fields: limit -func (_m *IntervalUpdater) IntervalsWithLimit(limit int) ([]models.Interval, error) { - ret := _m.Called(limit) - - var r0 []models.Interval - if rf, ok := ret.Get(0).(func(int) []models.Interval); ok { - r0 = rf(limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Interval) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateInterval provides a mock function with given fields: _a0 -func (_m *IntervalUpdater) UpdateInterval(_a0 models.Interval) error { - ret := _m.Called(_a0) - - var r0 error - if rf, ok := ret.Get(0).(func(models.Interval) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/internal/support/scheduler/operators/interval/mocks/IntervalWriter.go b/internal/support/scheduler/operators/interval/mocks/IntervalWriter.go deleted file mode 100644 index da4345109a..0000000000 --- a/internal/support/scheduler/operators/interval/mocks/IntervalWriter.go +++ /dev/null @@ -1,120 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// IntervalWriter is an autogenerated mock type for the IntervalWriter type -type IntervalWriter struct { - mock.Mock -} - -// AddInterval provides a mock function with given fields: _a0 -func (_m *IntervalWriter) AddInterval(_a0 models.Interval) (string, error) { - ret := _m.Called(_a0) - - var r0 string - if rf, ok := ret.Get(0).(func(models.Interval) string); ok { - r0 = rf(_a0) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.Interval) error); ok { - r1 = rf(_a0) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalById provides a mock function with given fields: id -func (_m *IntervalWriter) IntervalById(id string) (models.Interval, error) { - ret := _m.Called(id) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalByName provides a mock function with given fields: name -func (_m *IntervalWriter) IntervalByName(name string) (models.Interval, error) { - ret := _m.Called(name) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(name) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Intervals provides a mock function with given fields: -func (_m *IntervalWriter) Intervals() ([]models.Interval, error) { - ret := _m.Called() - - var r0 []models.Interval - if rf, ok := ret.Get(0).(func() []models.Interval); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Interval) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalsWithLimit provides a mock function with given fields: limit -func (_m *IntervalWriter) IntervalsWithLimit(limit int) ([]models.Interval, error) { - ret := _m.Called(limit) - - var r0 []models.Interval - if rf, ok := ret.Get(0).(func(int) []models.Interval); ok { - r0 = rf(limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Interval) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/support/scheduler/operators/interval/mocks/SchedulerQueueDeleter.go b/internal/support/scheduler/operators/interval/mocks/SchedulerQueueDeleter.go deleted file mode 100644 index e503ae0407..0000000000 --- a/internal/support/scheduler/operators/interval/mocks/SchedulerQueueDeleter.go +++ /dev/null @@ -1,67 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// SchedulerQueueDeleter is an autogenerated mock type for the SchedulerQueueDeleter type -type SchedulerQueueDeleter struct { - mock.Mock -} - -// QueryIntervalByID provides a mock function with given fields: intervalId -func (_m *SchedulerQueueDeleter) QueryIntervalByID(intervalId string) (models.Interval, error) { - ret := _m.Called(intervalId) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(intervalId) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(intervalId) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// QueryIntervalByName provides a mock function with given fields: intervalName -func (_m *SchedulerQueueDeleter) QueryIntervalByName(intervalName string) (models.Interval, error) { - ret := _m.Called(intervalName) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(intervalName) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(intervalName) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RemoveIntervalInQueue provides a mock function with given fields: intervalId -func (_m *SchedulerQueueDeleter) RemoveIntervalInQueue(intervalId string) error { - ret := _m.Called(intervalId) - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(intervalId) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/internal/support/scheduler/operators/interval/mocks/SchedulerQueueLoader.go b/internal/support/scheduler/operators/interval/mocks/SchedulerQueueLoader.go deleted file mode 100644 index 96d2fa37d5..0000000000 --- a/internal/support/scheduler/operators/interval/mocks/SchedulerQueueLoader.go +++ /dev/null @@ -1,53 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// SchedulerQueueLoader is an autogenerated mock type for the SchedulerQueueLoader type -type SchedulerQueueLoader struct { - mock.Mock -} - -// QueryIntervalByID provides a mock function with given fields: intervalId -func (_m *SchedulerQueueLoader) QueryIntervalByID(intervalId string) (models.Interval, error) { - ret := _m.Called(intervalId) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(intervalId) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(intervalId) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// QueryIntervalByName provides a mock function with given fields: intervalName -func (_m *SchedulerQueueLoader) QueryIntervalByName(intervalName string) (models.Interval, error) { - ret := _m.Called(intervalName) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(intervalName) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(intervalName) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/support/scheduler/operators/interval/mocks/SchedulerQueueUpdater.go b/internal/support/scheduler/operators/interval/mocks/SchedulerQueueUpdater.go deleted file mode 100644 index 1dc4377430..0000000000 --- a/internal/support/scheduler/operators/interval/mocks/SchedulerQueueUpdater.go +++ /dev/null @@ -1,67 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// SchedulerQueueUpdater is an autogenerated mock type for the SchedulerQueueUpdater type -type SchedulerQueueUpdater struct { - mock.Mock -} - -// QueryIntervalByID provides a mock function with given fields: intervalId -func (_m *SchedulerQueueUpdater) QueryIntervalByID(intervalId string) (models.Interval, error) { - ret := _m.Called(intervalId) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(intervalId) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(intervalId) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// QueryIntervalByName provides a mock function with given fields: intervalName -func (_m *SchedulerQueueUpdater) QueryIntervalByName(intervalName string) (models.Interval, error) { - ret := _m.Called(intervalName) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(intervalName) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(intervalName) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateIntervalInQueue provides a mock function with given fields: _a0 -func (_m *SchedulerQueueUpdater) UpdateIntervalInQueue(_a0 models.Interval) error { - ret := _m.Called(_a0) - - var r0 error - if rf, ok := ret.Get(0).(func(models.Interval) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/internal/support/scheduler/operators/interval/mocks/SchedulerQueueWriter.go b/internal/support/scheduler/operators/interval/mocks/SchedulerQueueWriter.go deleted file mode 100644 index 7d16419ea3..0000000000 --- a/internal/support/scheduler/operators/interval/mocks/SchedulerQueueWriter.go +++ /dev/null @@ -1,67 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// SchedulerQueueWriter is an autogenerated mock type for the SchedulerQueueWriter type -type SchedulerQueueWriter struct { - mock.Mock -} - -// AddIntervalToQueue provides a mock function with given fields: _a0 -func (_m *SchedulerQueueWriter) AddIntervalToQueue(_a0 models.Interval) error { - ret := _m.Called(_a0) - - var r0 error - if rf, ok := ret.Get(0).(func(models.Interval) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// QueryIntervalByID provides a mock function with given fields: intervalId -func (_m *SchedulerQueueWriter) QueryIntervalByID(intervalId string) (models.Interval, error) { - ret := _m.Called(intervalId) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(intervalId) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(intervalId) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// QueryIntervalByName provides a mock function with given fields: intervalName -func (_m *SchedulerQueueWriter) QueryIntervalByName(intervalName string) (models.Interval, error) { - ret := _m.Called(intervalName) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(intervalName) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(intervalName) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/support/scheduler/operators/interval/update.go b/internal/support/scheduler/operators/interval/update.go deleted file mode 100644 index e9de90de23..0000000000 --- a/internal/support/scheduler/operators/interval/update.go +++ /dev/null @@ -1,105 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 interval - -import ( - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/errors" - "github.com/robfig/cron" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type UpdateExecutor interface { - Execute() error -} - -type intervalUpdate struct { - database IntervalUpdater - scClient SchedulerQueueUpdater - interval contract.Interval -} - -// This method updates the provided Addressable in the database. -func (op intervalUpdate) Execute() error { - to, err := op.database.IntervalById(op.interval.ID) - if err != nil { - // Check by name - to, err = op.database.IntervalByName(op.interval.Name) - if err != nil { - return errors.NewErrIntervalNotFound(op.interval.ID) - } - } - // Update the fields - if op.interval.Cron != "" { - if _, err := cron.Parse(op.interval.Cron); err != nil { - return errors.NewErrInvalidCronFormat(op.interval.Cron) - } - to.Cron = op.interval.Cron - } - if op.interval.Timestamps.Origin != 0 { - to.Timestamps.Origin = op.interval.Timestamps.Origin - } - // Check if new name is unique - if op.interval.Name != "" && op.interval.Name != to.Name { - checkInterval, err := op.database.IntervalByName(op.interval.Name) - // Check for error other than not found - if err != nil && err != db.ErrNotFound { - return err - } - // Check if interval with new name exists - if checkInterval.ID != "" { - return errors.NewErrIntervalNameInUse(op.interval.Name) - } - // Check if the interval still has attached interval actions - stillInUse, err := op.isIntervalStillInUse(to) - if err != nil { - return err - } - if stillInUse { - return errors.NewErrIntervalStillInUse(to.Name) - } - } - op.interval.ID = to.ID - err = op.scClient.UpdateIntervalInQueue(op.interval) - if err != nil { - return err - } - - return op.database.UpdateInterval(op.interval) -} - -// This factory method returns an executor used to update an addressable. -func NewUpdateExecutor(database IntervalUpdater, scClient SchedulerQueueUpdater, interval contract.Interval) UpdateExecutor { - return intervalUpdate{ - database: database, - scClient: scClient, - interval: interval, - } -} - -// Helper function -func (op intervalUpdate) isIntervalStillInUse(s contract.Interval) (bool, error) { - var intervalActions []contract.IntervalAction - - intervalActions, err := op.database.IntervalActionsByIntervalName(s.Name) - if err != nil { - return false, err - } - if len(intervalActions) > 0 { - return true, nil - } - - return false, nil -} diff --git a/internal/support/scheduler/operators/interval/update_test.go b/internal/support/scheduler/operators/interval/update_test.go deleted file mode 100644 index 6275493704..0000000000 --- a/internal/support/scheduler/operators/interval/update_test.go +++ /dev/null @@ -1,261 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 interval - -import ( - "testing" - - intervalErrors "github.com/edgexfoundry/edgex-go/internal/support/scheduler/errors" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/operators/interval/mocks" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -var TestInvalidCron = "invalid" -var TestValidCron = "* * * ? * *" - -var IntervalHasInvalidCron = contract.Interval{ - - ID: Id, - Name: "hourly", - Start: "20160101T000000", - End: "", - Frequency: "PT1H", - Cron: TestInvalidCron, -} - -var IntervalHasValidCron = contract.Interval{ - - ID: Id, - Name: OtherName, - Start: "20160101T000000", - End: "", - Frequency: "PT1H", - Cron: TestValidCron, - Timestamps: contract.Timestamps{Origin: 201601011565351081}, -} - -func TestUpdateExecutor(t *testing.T) { - successNoId := SuccessfulDatabaseResult[0] - successNoId.Name = successNoId.ID - successNoId.ID = "" - - successNewName := SuccessfulDatabaseResult[0] - successNewName.Name = "something different" - - tests := []struct { - name string - dbMock IntervalUpdater - scClient SchedulerQueueUpdater - interval contract.Interval - expectedError bool - expectedErrorVal error - }{ - { - name: "IntervalActionsByIntervalName success", - dbMock: createMockIntervalUpdaterIntvActionSuccess(), - scClient: createMockIntervalUpdaterSCSuccess(SuccessfulDatabaseResult[0]), - interval: SuccessfulDatabaseResult[0], - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "IntervalActionsByIntervalName In Use", - dbMock: createMockIntervalUpdaterIntvActionInUse(), - scClient: createMockIntervalUpdaterSCSuccess(SuccessfulDatabaseResult[0]), - interval: SuccessfulDatabaseResult[0], - expectedError: true, - expectedErrorVal: intervalErrors.NewErrIntervalStillInUse(IntervalHasValidCron.Name), - }, - { - name: "IntervalActionsByIntervalName Error", - dbMock: createMockIntervalUpdaterIntvActionErr(), - scClient: createMockIntervalUpdaterSCSuccess(SuccessfulDatabaseResult[0]), - interval: SuccessfulDatabaseResult[0], - expectedError: true, - expectedErrorVal: Error, - }, - { - name: "IntervalByName Error", - dbMock: createMockIntervalUpdaterNameErr(), - scClient: createMockIntervalUpdaterSCSuccess(SuccessfulDatabaseResult[0]), - interval: SuccessfulDatabaseResult[0], - expectedError: true, - expectedErrorVal: Error, - }, - { - name: "IntervalNameInUseErr", - dbMock: createMockIntervalUpdaterNameInUseErr(), - scClient: createMockIntervalUpdaterSCSuccess(SuccessfulDatabaseResult[0]), - interval: SuccessfulDatabaseResult[0], - expectedError: true, - expectedErrorVal: intervalErrors.NewErrIntervalNameInUse(SuccessfulDatabaseResult[0].Name), - }, - { - name: "Successful database call, search by ID", - dbMock: createMockIntervalUpdaterSuccess(), - scClient: createMockIntervalUpdaterSCSuccess(SuccessfulDatabaseResult[0]), - interval: SuccessfulDatabaseResult[0], - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "Not found by ID, Not found by Name", - dbMock: createMockIntervalUpdaterNotFoundErr(), - scClient: createMockIntervalUpdaterSCSuccess(SuccessfulDatabaseResult[0]), - interval: SuccessfulDatabaseResult[0], - expectedError: true, - expectedErrorVal: intervalErrors.NewErrIntervalNotFound(SuccessfulDatabaseResult[0].ID), - }, - { - name: "Error Cron", - dbMock: createMockIntervalUpdaterCronErr(), - scClient: createMockIntervalUpdaterSCSuccess(SuccessfulDatabaseResult[0]), - interval: IntervalHasInvalidCron, - expectedError: true, - expectedErrorVal: intervalErrors.NewErrInvalidCronFormat(TestInvalidCron), - }, - { - name: "Cron is valid", - dbMock: createMockIntervalUpdaterCronValid(), - scClient: createMockIntervalUpdaterSCSuccess(IntervalHasValidCron), - interval: IntervalHasValidCron, - expectedError: false, - expectedErrorVal: nil, - }, - - { - name: "Unexpected error in UpdateIntervalInQueue", - dbMock: createMockIntervalUpdaterSuccess(), - scClient: createMockIntervalUpdaterSCErr(SuccessfulDatabaseResult[0]), - interval: SuccessfulDatabaseResult[0], - expectedError: true, - expectedErrorVal: Error, - }, - } - - for _, test := range tests { - t.Run(test.name, func(tt *testing.T) { - op := NewUpdateExecutor(test.dbMock, test.scClient, test.interval) - err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -func createMockIntervalUpdaterSuccess() IntervalUpdater { - dbMock := mocks.IntervalUpdater{} - dbMock.On("IntervalById", Id).Return(SuccessfulDatabaseResult[0], nil) - dbMock.On("IntervalByName", Name).Return(SuccessfulDatabaseResult[0], nil) - dbMock.On("IntervalActionsByIntervalName", Name).Return([]contract.IntervalAction{}, nil) - dbMock.On("UpdateInterval", SuccessfulDatabaseResult[0]).Return(nil) - return &dbMock -} - -func createMockIntervalUpdaterNameInUseErr() IntervalUpdater { - dbMock := mocks.IntervalUpdater{} - dbMock.On("IntervalById", Id).Return(IntervalHasValidCron, nil) - dbMock.On("IntervalByName", Name).Return(IntervalHasValidCron, nil) - dbMock.On("IntervalActionsByIntervalName", OtherName).Return([]contract.IntervalAction{}, nil) - dbMock.On("UpdateInterval", SuccessfulDatabaseResult[0]).Return(nil) - return &dbMock -} - -func createMockIntervalUpdaterNameErr() IntervalUpdater { - dbMock := mocks.IntervalUpdater{} - dbMock.On("IntervalById", Id).Return(IntervalHasValidCron, nil) - dbMock.On("IntervalByName", Name).Return(IntervalHasValidCron, Error) - dbMock.On("IntervalActionsByIntervalName", OtherName).Return([]contract.IntervalAction{}, nil) - dbMock.On("UpdateInterval", SuccessfulDatabaseResult[0]).Return(nil) - return &dbMock -} - -func createMockIntervalUpdaterIntvActionErr() IntervalUpdater { - dbMock := mocks.IntervalUpdater{} - dbMock.On("IntervalById", Id).Return(IntervalHasValidCron, nil) - dbMock.On("IntervalByName", Name).Return(contract.Interval{}, nil) - dbMock.On("IntervalActionsByIntervalName", OtherName).Return([]contract.IntervalAction{}, Error) - dbMock.On("UpdateInterval", SuccessfulDatabaseResult[0]).Return(nil) - return &dbMock -} - -func createMockIntervalUpdaterIntvActionInUse() IntervalUpdater { - dbMock := mocks.IntervalUpdater{} - dbMock.On("IntervalById", Id).Return(IntervalHasValidCron, nil) - dbMock.On("IntervalByName", Name).Return(contract.Interval{}, nil) - dbMock.On("IntervalActionsByIntervalName", OtherName).Return(SuccessfulIntervalActionResult, nil) - dbMock.On("UpdateInterval", SuccessfulDatabaseResult[0]).Return(nil) - return &dbMock -} - -func createMockIntervalUpdaterIntvActionSuccess() IntervalUpdater { - dbMock := mocks.IntervalUpdater{} - dbMock.On("IntervalById", Id).Return(IntervalHasValidCron, nil) - dbMock.On("IntervalByName", Name).Return(contract.Interval{}, nil) - dbMock.On("IntervalActionsByIntervalName", OtherName).Return([]contract.IntervalAction{}, nil) - dbMock.On("UpdateInterval", SuccessfulDatabaseResult[0]).Return(nil) - return &dbMock -} - -func createMockIntervalUpdaterCronErr() IntervalUpdater { - dbMock := mocks.IntervalUpdater{} - dbMock.On("IntervalById", Id).Return(IntervalHasInvalidCron, nil) - dbMock.On("IntervalByName", Name).Return(IntervalHasInvalidCron, nil) - dbMock.On("IntervalActionsByIntervalName", Name).Return([]contract.IntervalAction{}, nil) - dbMock.On("UpdateInterval", IntervalHasInvalidCron).Return(nil) - return &dbMock -} - -func createMockIntervalUpdaterCronValid() IntervalUpdater { - dbMock := mocks.IntervalUpdater{} - dbMock.On("IntervalById", Id).Return(IntervalHasValidCron, nil) - dbMock.On("IntervalByName", Name).Return(IntervalHasValidCron, nil) - dbMock.On("IntervalActionsByIntervalName", Name).Return([]contract.IntervalAction{}, nil) - dbMock.On("UpdateInterval", IntervalHasValidCron).Return(nil) - return &dbMock -} - -func createMockIntervalUpdaterNotFoundErr() IntervalUpdater { - dbMock := mocks.IntervalUpdater{} - dbMock.On("IntervalById", Id).Return(contract.Interval{}, ErrorNotFound) - dbMock.On("IntervalByName", Name).Return(contract.Interval{}, ErrorNotFound) - dbMock.On("IntervalActionsByIntervalName", Name).Return([]contract.IntervalAction{}, nil) - dbMock.On("UpdateInterval", IntervalHasInvalidCron).Return(nil) - return &dbMock -} - -func createMockIntervalUpdaterSCSuccess(interval contract.Interval) SchedulerQueueUpdater { - dbMock := mocks.SchedulerQueueUpdater{} - dbMock.On("UpdateIntervalInQueue", interval).Return(nil) - return &dbMock -} - -func createMockIntervalUpdaterSCErr(interval contract.Interval) SchedulerQueueUpdater { - dbMock := mocks.SchedulerQueueUpdater{} - dbMock.On("UpdateIntervalInQueue", interval).Return(Error) - return &dbMock -} diff --git a/internal/support/scheduler/operators/intervalaction/add.go b/internal/support/scheduler/operators/intervalaction/add.go deleted file mode 100644 index 0fea7f8e6a..0000000000 --- a/internal/support/scheduler/operators/intervalaction/add.go +++ /dev/null @@ -1,96 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 intervalaction - -import ( - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/errors" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -type AddExecutor interface { - Execute() (string, error) -} - -type intervalActionAdd struct { - database IntervalActionWriter - intervalAction contract.IntervalAction - scClient SchedulerQueueWriter -} - -// This method adds the provided Addressable to the database. -func (op intervalActionAdd) Execute() (id string, err error) { - newId, err := addNewIntervalAction(op) - if err != nil { - return newId, err - } - return newId, nil -} - -// This factory method returns an executor used to add an addressable. -func NewAddExecutor(db IntervalActionWriter, scClient SchedulerQueueWriter, intervalAction contract.IntervalAction) AddExecutor { - return intervalActionAdd{ - database: db, - scClient: scClient, - intervalAction: intervalAction, - } -} - -func addNewIntervalAction(iaa intervalActionAdd) (string, error) { - name := iaa.intervalAction.Name - - // Validate the IntervalAction is not in use - ret, err := iaa.database.IntervalActionByName(name) - if err == nil && ret.Name == name { - return "", errors.NewErrIntervalActionNameInUse(name) - } - - // Validate the Target - target := iaa.intervalAction.Target - if target == "" { - return "", errors.NewErrIntervalActionTargetNameRequired(iaa.intervalAction.ID) - } - - // Validate the Interval - interval := iaa.intervalAction.Interval - if interval != "" { - _, err := iaa.database.IntervalByName(interval) - if err != nil { - return "", errors.NewErrIntervalNotFound(interval) - } - } else { - return "", errors.NewErrIntervalNotFound(iaa.intervalAction.ID) - } - - // Validate the IntervalAction does not exist in the scheduler queue - retQ, err := iaa.scClient.QueryIntervalActionByName(name) - if err == nil && retQ.Name == name { - return "", errors.NewErrIntervalActionNameInUse(name) - } - - // Add the new Interval Action to the database - ID, err := iaa.database.AddIntervalAction(iaa.intervalAction) - if err != nil { - return "", err - } - - iaa.intervalAction.ID = ID - - // Add the new IntervalAction into scheduler queue - err = iaa.scClient.AddIntervalActionToQueue(iaa.intervalAction) - if err != nil { - return "", err - } - - return ID, nil -} diff --git a/internal/support/scheduler/operators/intervalaction/add_test.go b/internal/support/scheduler/operators/intervalaction/add_test.go deleted file mode 100644 index 5a9e37a88b..0000000000 --- a/internal/support/scheduler/operators/intervalaction/add_test.go +++ /dev/null @@ -1,207 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 intervalaction - -import ( - "reflect" - "testing" - - intervalErrors "github.com/edgexfoundry/edgex-go/internal/support/scheduler/errors" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/operators/intervalaction/mocks" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -//var InvalidFreqInterval = SuccessfulIntervalActionResult[4] - -func TestAddExecutor(t *testing.T) { - - tests := []struct { - name string - mockDb IntervalActionWriter - scClient SchedulerQueueWriter - intervalAction contract.IntervalAction - expectedResult string - expectedError bool - expectedErrorVal error - }{ - { - name: "Successful database call", - mockDb: createAddMockIntervalActionSuccess(), - scClient: createAddMockIntervalSCSuccess(), - intervalAction: ValidIntervalAction, - expectedResult: ValidIntervalAction.ID, - expectedError: false, - expectedErrorVal: nil, - }, - { - name: "Error IntervalActionByName", - mockDb: createAddMockIntervalActionByNameSameErr(), - scClient: createAddMockIntervalSCSuccess(), - intervalAction: ValidIntervalAction, - expectedResult: "", - expectedError: true, - expectedErrorVal: intervalErrors.NewErrIntervalActionNameInUse(ValidIntervalAction.Name), - }, - { - name: "Error IntervalByName", - mockDb: createAddMockIntervalActionByNameErr(), - scClient: createAddMockIntervalSCSuccess(), - intervalAction: ValidIntervalAction, - expectedResult: "", - expectedError: true, - expectedErrorVal: intervalErrors.NewErrIntervalNotFound(Intervals[0].Name), - }, - { - name: "Error No Target", - mockDb: createAddMockIntervalActionTargetErr(), - scClient: createAddMockIntervalSCSuccess(), - intervalAction: InvalidIntervalAction, - expectedResult: "", - expectedError: true, - expectedErrorVal: intervalErrors.NewErrIntervalActionTargetNameRequired(InvalidIntervalAction.ID), - }, - { - name: "Error No Interval", - mockDb: createAddMockIntervalActionNoIntervalErr(), - scClient: createAddMockIntervalSCSuccess(), - intervalAction: IntervalActionNoInterval, - expectedResult: "", - expectedError: true, - expectedErrorVal: intervalErrors.NewErrIntervalNotFound(IntervalActionNoInterval.ID), - }, - { - name: "Error QueryIntervalActionByName", - mockDb: createAddMockIntervalActionSuccess(), - scClient: createAddMockIntervalSCQueryError(), - intervalAction: ValidIntervalAction, - expectedResult: "", - expectedError: true, - expectedErrorVal: intervalErrors.NewErrIntervalActionNameInUse(ValidIntervalAction.Name), - }, - { - name: "Error AddIntervalActionToQueue", - mockDb: createAddMockIntervalActionSuccess(), - scClient: createAddMockIntervalSCAddError(), - intervalAction: ValidIntervalAction, - expectedResult: "", - expectedError: true, - expectedErrorVal: Error, - }, - { - name: "Error AddIntervalAction", - mockDb: createAddMockIntervalActionAddErr(), - scClient: createAddMockIntervalSCSuccess(), - intervalAction: ValidIntervalAction, - expectedResult: "", - expectedError: true, - expectedErrorVal: Error, - }, - } - - for _, test := range tests { - t.Run(test.name, func(tt *testing.T) { - op := NewAddExecutor(test.mockDb, test.scClient, test.intervalAction) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - - if test.expectedErrorVal != nil && err != nil { - if test.expectedErrorVal.Error() != err.Error() { - t.Errorf("Observed error doesn't match expected.\nExpected: %v\nActual: %v\n", test.expectedErrorVal.Error(), err.Error()) - } - } - }) - } -} - -func createAddMockIntervalActionSuccess() IntervalActionWriter { - dbMock := mocks.IntervalActionWriter{} - dbMock.On("IntervalActionByName", ValidIntervalAction.Name).Return(OtherValidIntervalAction, nil) - dbMock.On("IntervalByName", Intervals[0].Name).Return(Intervals[0], nil) - dbMock.On("AddIntervalAction", ValidIntervalAction).Return(ValidIntervalAction.ID, nil) - return &dbMock -} - -func createAddMockIntervalActionAddErr() IntervalActionWriter { - dbMock := mocks.IntervalActionWriter{} - dbMock.On("IntervalActionByName", ValidIntervalAction.Name).Return(OtherValidIntervalAction, nil) - dbMock.On("IntervalByName", Intervals[0].Name).Return(Intervals[0], nil) - dbMock.On("AddIntervalAction", ValidIntervalAction).Return(ValidIntervalAction.ID, Error) - return &dbMock -} - -func createAddMockIntervalActionByNameSameErr() IntervalActionWriter { - dbMock := mocks.IntervalActionWriter{} - dbMock.On("IntervalActionByName", ValidIntervalAction.Name).Return(ValidIntervalAction, nil) - dbMock.On("IntervalByName", Intervals[0].Name).Return(Intervals[0], nil) - dbMock.On("AddIntervalAction", ValidIntervalAction).Return(ValidIntervalAction.ID, nil) - return &dbMock -} - -func createAddMockIntervalActionByNameErr() IntervalActionWriter { - dbMock := mocks.IntervalActionWriter{} - dbMock.On("IntervalActionByName", ValidIntervalAction.Name).Return(OtherValidIntervalAction, nil) - dbMock.On("IntervalByName", Intervals[0].Name).Return(Intervals[0], Error) - dbMock.On("AddIntervalAction", ValidIntervalAction).Return(ValidIntervalAction.ID, nil) - return &dbMock -} - -func createAddMockIntervalActionTargetErr() IntervalActionWriter { - dbMock := mocks.IntervalActionWriter{} - dbMock.On("IntervalActionByName", InvalidIntervalAction.Name).Return(OtherValidIntervalAction, nil) - dbMock.On("IntervalByName", Intervals[0].Name).Return(Intervals[0], nil) - dbMock.On("AddIntervalAction", InvalidIntervalAction).Return(InvalidIntervalAction.ID, nil) - return &dbMock -} - -func createAddMockIntervalActionNoIntervalErr() IntervalActionWriter { - dbMock := mocks.IntervalActionWriter{} - dbMock.On("IntervalActionByName", IntervalActionNoInterval.Name).Return(OtherValidIntervalAction, nil) - dbMock.On("IntervalByName", Intervals[0].Name).Return(Intervals[0], nil) - dbMock.On("AddIntervalAction", IntervalActionNoInterval).Return(IntervalActionNoInterval.ID, nil) - return &dbMock -} - -func createAddMockIntervalSCSuccess() SchedulerQueueWriter { - dbMock := mocks.SchedulerQueueWriter{} - dbMock.On("QueryIntervalActionByName", ValidIntervalAction.Name).Return(OtherValidIntervalAction, nil) - dbMock.On("AddIntervalActionToQueue", ValidIntervalAction).Return(nil) - return &dbMock -} - -func createAddMockIntervalSCQueryError() SchedulerQueueWriter { - dbMock := mocks.SchedulerQueueWriter{} - dbMock.On("QueryIntervalActionByName", ValidIntervalAction.Name).Return(ValidIntervalAction, nil) - dbMock.On("AddIntervalActionToQueue", ValidIntervalAction).Return(Error) - return &dbMock -} - -func createAddMockIntervalSCAddError() SchedulerQueueWriter { - dbMock := mocks.SchedulerQueueWriter{} - dbMock.On("QueryIntervalActionByName", ValidIntervalAction.Name).Return(OtherValidIntervalAction, nil) - dbMock.On("AddIntervalActionToQueue", ValidIntervalAction).Return(Error) - return &dbMock -} diff --git a/internal/support/scheduler/operators/intervalaction/db.go b/internal/support/scheduler/operators/intervalaction/db.go deleted file mode 100644 index f7b17067fe..0000000000 --- a/internal/support/scheduler/operators/intervalaction/db.go +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 intervalaction - -import ( - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/operators/interval" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -// IntervalLoader provides functionality for obtaining Interval. -type IntervalActionLoader interface { - IntervalActions() ([]contract.IntervalAction, error) - IntervalActionsWithLimit(limit int) ([]contract.IntervalAction, error) - IntervalActionByName(name string) (contract.IntervalAction, error) - IntervalActionById(id string) (contract.IntervalAction, error) - interval.IntervalLoader -} - -// IntervalWriter adds interval. -type IntervalActionWriter interface { - AddIntervalAction(interval contract.IntervalAction) (string, error) - IntervalActionLoader -} - -type SchedulerQueueLoader interface { - QueryIntervalActionByID(intervalActionId string) (contract.IntervalAction, error) - QueryIntervalActionByName(intervalActionName string) (contract.IntervalAction, error) -} - -// SchedulerQueueWriter adds interval in SchedulerQueue -type SchedulerQueueWriter interface { - AddIntervalActionToQueue(interval contract.IntervalAction) error - SchedulerQueueLoader -} diff --git a/internal/support/scheduler/operators/intervalaction/get.go b/internal/support/scheduler/operators/intervalaction/get.go deleted file mode 100644 index 179395baf7..0000000000 --- a/internal/support/scheduler/operators/intervalaction/get.go +++ /dev/null @@ -1,55 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 intervalaction - -import ( - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/errors" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -// This interface that returns a collection of interval actions -type IntervalActionsExecutor interface { - Execute() ([]contract.IntervalAction, error) -} - -// This type is for getting interval actions from the database with a given limit. -type intervalActionLoadAll struct { - database IntervalActionLoader - config bootstrapConfig.ServiceInfo -} - -// This method gets interval actions from the database. -func (op intervalActionLoadAll) Execute() ([]contract.IntervalAction, error) { - intervalActions, err := op.database.IntervalActions() - - if err != nil { - return intervalActions, err - } - if len(intervalActions) > op.config.MaxResultCount { - return nil, errors.NewErrLimitExceeded(len(intervalActions)) - } - return intervalActions, nil -} - -// This factory method returns an executor used to get interval actions. -func NewAllExecutor(db IntervalActionLoader, config bootstrapConfig.ServiceInfo) IntervalActionsExecutor { - return intervalActionLoadAll{ - database: db, - config: config, - } -} diff --git a/internal/support/scheduler/operators/intervalaction/get_test.go b/internal/support/scheduler/operators/intervalaction/get_test.go deleted file mode 100644 index 4386aa875f..0000000000 --- a/internal/support/scheduler/operators/intervalaction/get_test.go +++ /dev/null @@ -1,176 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 intervalaction - -import ( - "errors" - "reflect" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/operators/intervalaction/mocks" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -var Id = "83cb038b-5a94-4707-985d-13effec62de2" -var Name = "hourly" -var OtherName = "other" -var Error = errors.New("test error") -var ErrorNotFound = db.ErrNotFound -var TestLimit = 20 -var TestServiceConfig = bootstrapConfig.ServiceInfo{ - MaxResultCount: TestLimit, -} -var Intervals = []contract.Interval{ - { - ID: Id, - Name: "hourly", - Start: "20160101T000000", - End: "", - Frequency: "PT1H", - }, -} - -var IntervalActions = []contract.IntervalAction{ - { - ID: Id, - Name: "scrub pushed records", - Interval: "hourly", - Parameters: "", - Target: "test target", - }, - { - ID: Id, - Name: "scrub pushed records 2", - Interval: "hourly", - Parameters: "", - Target: "test target", - }, - { - ID: Id, - Name: "scrub pushed records 3", - Interval: "hourly", - Parameters: "", - }, - { - ID: Id, - Name: "scrub pushed records 4", - Interval: "", - Target: "test target", - Parameters: "", - }, - { - ID: Id, - Interval: "hourly", - Parameters: "", - Target: "test target", - }, - { - ID: Id, - Name: "scrub pushed records 6", - Interval: "hourly", - Parameters: "", - }, -} - -var ValidIntervalAction = IntervalActions[0] -var OtherValidIntervalAction = IntervalActions[1] -var InvalidIntervalAction = IntervalActions[2] -var IntervalActionNoInterval = IntervalActions[3] -var IntervalActionNoName = IntervalActions[4] -var IntervalActionNoTarget = IntervalActions[5] - -func createMockIntervalActionsSuccess() IntervalActionLoader { - dbMock := mocks.IntervalActionLoader{} - dbMock.On("IntervalActions").Return(IntervalActions, nil) - return &dbMock -} - -func createMockIntervalActionsExceedErr() IntervalActionLoader { - dbMock := mocks.IntervalActionLoader{} - dbMock.On("IntervalActions").Return(createIntervalActions(21), nil) - return &dbMock -} - -func createMockIntervalActionsFail() IntervalActionLoader { - dbMock := mocks.IntervalActionLoader{} - dbMock.On("IntervalActions").Return([]contract.IntervalAction{}, Error) - return &dbMock -} - -func TestAllExecutor(t *testing.T) { - tests := []struct { - name string - mockDb IntervalActionLoader - expectedResult []contract.IntervalAction - expectedError bool - }{ - { - name: "Successful database call", - mockDb: createMockIntervalActionsSuccess(), - expectedResult: IntervalActions, - expectedError: false, - }, - { - name: "Exceed limit error", - mockDb: createMockIntervalActionsExceedErr(), - expectedResult: nil, - expectedError: true, - }, - { - name: "Unexpected error", - mockDb: createMockIntervalActionsFail(), - expectedResult: []contract.IntervalAction{}, - expectedError: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - op := NewAllExecutor(test.mockDb, TestServiceConfig) - actual, err := op.Execute() - if test.expectedError && err == nil { - t.Error("Expected an error") - return - } - - if !test.expectedError && err != nil { - t.Errorf("Unexpectedly encountered error: %s", err.Error()) - return - } - - if !reflect.DeepEqual(test.expectedResult, actual) { - t.Errorf("Expected result does not match the observed.\nExpected: %v\nObserved: %v\n", test.expectedResult, actual) - return - } - }) - } -} - -func createIntervalActions(howMany int) []contract.IntervalAction { - var intervals []contract.IntervalAction - for i := 0; i < howMany; i++ { - intervals = append(intervals, contract.IntervalAction{ - ID: Id, - Name: "scrub pushed records", - Interval: "hourly", - Parameters: "", - }) - } - return intervals -} diff --git a/internal/support/scheduler/operators/intervalaction/mocks/IntervalActionLoader.go b/internal/support/scheduler/operators/intervalaction/mocks/IntervalActionLoader.go deleted file mode 100644 index ef5aa6c02d..0000000000 --- a/internal/support/scheduler/operators/intervalaction/mocks/IntervalActionLoader.go +++ /dev/null @@ -1,187 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// IntervalActionLoader is an autogenerated mock type for the IntervalActionLoader type -type IntervalActionLoader struct { - mock.Mock -} - -// IntervalActionById provides a mock function with given fields: id -func (_m *IntervalActionLoader) IntervalActionById(id string) (models.IntervalAction, error) { - ret := _m.Called(id) - - var r0 models.IntervalAction - if rf, ok := ret.Get(0).(func(string) models.IntervalAction); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.IntervalAction) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalActionByName provides a mock function with given fields: name -func (_m *IntervalActionLoader) IntervalActionByName(name string) (models.IntervalAction, error) { - ret := _m.Called(name) - - var r0 models.IntervalAction - if rf, ok := ret.Get(0).(func(string) models.IntervalAction); ok { - r0 = rf(name) - } else { - r0 = ret.Get(0).(models.IntervalAction) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalActions provides a mock function with given fields: -func (_m *IntervalActionLoader) IntervalActions() ([]models.IntervalAction, error) { - ret := _m.Called() - - var r0 []models.IntervalAction - if rf, ok := ret.Get(0).(func() []models.IntervalAction); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.IntervalAction) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalActionsWithLimit provides a mock function with given fields: limit -func (_m *IntervalActionLoader) IntervalActionsWithLimit(limit int) ([]models.IntervalAction, error) { - ret := _m.Called(limit) - - var r0 []models.IntervalAction - if rf, ok := ret.Get(0).(func(int) []models.IntervalAction); ok { - r0 = rf(limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.IntervalAction) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalById provides a mock function with given fields: id -func (_m *IntervalActionLoader) IntervalById(id string) (models.Interval, error) { - ret := _m.Called(id) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalByName provides a mock function with given fields: name -func (_m *IntervalActionLoader) IntervalByName(name string) (models.Interval, error) { - ret := _m.Called(name) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(name) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Intervals provides a mock function with given fields: -func (_m *IntervalActionLoader) Intervals() ([]models.Interval, error) { - ret := _m.Called() - - var r0 []models.Interval - if rf, ok := ret.Get(0).(func() []models.Interval); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Interval) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalsWithLimit provides a mock function with given fields: limit -func (_m *IntervalActionLoader) IntervalsWithLimit(limit int) ([]models.Interval, error) { - ret := _m.Called(limit) - - var r0 []models.Interval - if rf, ok := ret.Get(0).(func(int) []models.Interval); ok { - r0 = rf(limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Interval) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/support/scheduler/operators/intervalaction/mocks/IntervalActionWriter.go b/internal/support/scheduler/operators/intervalaction/mocks/IntervalActionWriter.go deleted file mode 100644 index ce3fdb5460..0000000000 --- a/internal/support/scheduler/operators/intervalaction/mocks/IntervalActionWriter.go +++ /dev/null @@ -1,208 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// IntervalActionWriter is an autogenerated mock type for the IntervalActionWriter type -type IntervalActionWriter struct { - mock.Mock -} - -// AddIntervalAction provides a mock function with given fields: interval -func (_m *IntervalActionWriter) AddIntervalAction(interval models.IntervalAction) (string, error) { - ret := _m.Called(interval) - - var r0 string - if rf, ok := ret.Get(0).(func(models.IntervalAction) string); ok { - r0 = rf(interval) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(models.IntervalAction) error); ok { - r1 = rf(interval) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalActionById provides a mock function with given fields: id -func (_m *IntervalActionWriter) IntervalActionById(id string) (models.IntervalAction, error) { - ret := _m.Called(id) - - var r0 models.IntervalAction - if rf, ok := ret.Get(0).(func(string) models.IntervalAction); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.IntervalAction) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalActionByName provides a mock function with given fields: name -func (_m *IntervalActionWriter) IntervalActionByName(name string) (models.IntervalAction, error) { - ret := _m.Called(name) - - var r0 models.IntervalAction - if rf, ok := ret.Get(0).(func(string) models.IntervalAction); ok { - r0 = rf(name) - } else { - r0 = ret.Get(0).(models.IntervalAction) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalActions provides a mock function with given fields: -func (_m *IntervalActionWriter) IntervalActions() ([]models.IntervalAction, error) { - ret := _m.Called() - - var r0 []models.IntervalAction - if rf, ok := ret.Get(0).(func() []models.IntervalAction); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.IntervalAction) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalActionsWithLimit provides a mock function with given fields: limit -func (_m *IntervalActionWriter) IntervalActionsWithLimit(limit int) ([]models.IntervalAction, error) { - ret := _m.Called(limit) - - var r0 []models.IntervalAction - if rf, ok := ret.Get(0).(func(int) []models.IntervalAction); ok { - r0 = rf(limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.IntervalAction) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalById provides a mock function with given fields: id -func (_m *IntervalActionWriter) IntervalById(id string) (models.Interval, error) { - ret := _m.Called(id) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(id) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalByName provides a mock function with given fields: name -func (_m *IntervalActionWriter) IntervalByName(name string) (models.Interval, error) { - ret := _m.Called(name) - - var r0 models.Interval - if rf, ok := ret.Get(0).(func(string) models.Interval); ok { - r0 = rf(name) - } else { - r0 = ret.Get(0).(models.Interval) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Intervals provides a mock function with given fields: -func (_m *IntervalActionWriter) Intervals() ([]models.Interval, error) { - ret := _m.Called() - - var r0 []models.Interval - if rf, ok := ret.Get(0).(func() []models.Interval); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Interval) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IntervalsWithLimit provides a mock function with given fields: limit -func (_m *IntervalActionWriter) IntervalsWithLimit(limit int) ([]models.Interval, error) { - ret := _m.Called(limit) - - var r0 []models.Interval - if rf, ok := ret.Get(0).(func(int) []models.Interval); ok { - r0 = rf(limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Interval) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(int) error); ok { - r1 = rf(limit) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/support/scheduler/operators/intervalaction/mocks/SchedulerQueueWriter.go b/internal/support/scheduler/operators/intervalaction/mocks/SchedulerQueueWriter.go deleted file mode 100644 index 8bdbc856b7..0000000000 --- a/internal/support/scheduler/operators/intervalaction/mocks/SchedulerQueueWriter.go +++ /dev/null @@ -1,67 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" -import models "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - -// SchedulerQueueWriter is an autogenerated mock type for the SchedulerQueueWriter type -type SchedulerQueueWriter struct { - mock.Mock -} - -// AddIntervalActionToQueue provides a mock function with given fields: interval -func (_m *SchedulerQueueWriter) AddIntervalActionToQueue(interval models.IntervalAction) error { - ret := _m.Called(interval) - - var r0 error - if rf, ok := ret.Get(0).(func(models.IntervalAction) error); ok { - r0 = rf(interval) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// QueryIntervalActionByID provides a mock function with given fields: intervalActionId -func (_m *SchedulerQueueWriter) QueryIntervalActionByID(intervalActionId string) (models.IntervalAction, error) { - ret := _m.Called(intervalActionId) - - var r0 models.IntervalAction - if rf, ok := ret.Get(0).(func(string) models.IntervalAction); ok { - r0 = rf(intervalActionId) - } else { - r0 = ret.Get(0).(models.IntervalAction) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(intervalActionId) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// QueryIntervalActionByName provides a mock function with given fields: intervalActionName -func (_m *SchedulerQueueWriter) QueryIntervalActionByName(intervalActionName string) (models.IntervalAction, error) { - ret := _m.Called(intervalActionName) - - var r0 models.IntervalAction - if rf, ok := ret.Get(0).(func(string) models.IntervalAction); ok { - r0 = rf(intervalActionName) - } else { - r0 = ret.Get(0).(models.IntervalAction) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(intervalActionName) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/internal/support/scheduler/rest_interval.go b/internal/support/scheduler/rest_interval.go deleted file mode 100644 index 49d4923aca..0000000000 --- a/internal/support/scheduler/rest_interval.go +++ /dev/null @@ -1,325 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 scheduler - -import ( - "encoding/json" - "net/http" - "net/url" - "strconv" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/types" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/gorilla/mux" - - "github.com/edgexfoundry/edgex-go/internal/pkg" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/config" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/errors" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/interfaces" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/operators/interval" -) - -func restGetIntervals( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - configuration *config.ConfigurationStruct) { - - if r.Body != nil { - defer r.Body.Close() - } - - op := interval.NewAllExecutor(dbClient, configuration.Service.MaxResultCount) - intervals, err := op.Execute() - if err != nil { - lc.Error(err.Error()) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - pkg.Encode(intervals, w, lc) -} - -func restUpdateInterval( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - scClient interfaces.SchedulerQueueClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - var from models.Interval - dec := json.NewDecoder(r.Body) - err := dec.Decode(&from) - - // Problem decoding - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error("Error decoding the interval: " + err.Error()) - return - } - - lc.Info("Updating Interval: " + from.ID) - op := interval.NewUpdateExecutor(dbClient, scClient, from) - err = op.Execute() - if err != nil { - switch t := err.(type) { - case errors.ErrIntervalNotFound: - http.Error(w, t.Error(), http.StatusNotFound) - case errors.ErrInvalidCronFormat: - http.Error(w, t.Error(), http.StatusBadRequest) - case errors.ErrIntervalStillUsedByIntervalActions: - http.Error(w, t.Error(), http.StatusBadRequest) - case errors.ErrIntervalNameInUse: - http.Error(w, t.Error(), http.StatusBadRequest) - default: - http.Error(w, err.Error(), http.StatusServiceUnavailable) - } - lc.Error(err.Error()) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restAddInterval( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - scClient interfaces.SchedulerQueueClient) { - - if r.Body != nil { - defer r.Body.Close() - } - var intervalObj models.Interval - dec := json.NewDecoder(r.Body) - err := dec.Decode(&intervalObj) - - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error("Error decoding interval" + err.Error()) - return - } - lc.Info("Posting new Interval: " + intervalObj.String()) - - op := interval.NewAddExecutor(dbClient, scClient, intervalObj) - newId, err := op.Execute() - if err != nil { - switch t := err.(type) { - case errors.ErrIntervalNameInUse: - http.Error(w, t.Error(), http.StatusBadRequest) - default: - http.Error(w, t.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte(newId)) -} - -func restGetIntervalByID( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - // URL parameters - vars := mux.Vars(r) - id, err := url.QueryUnescape(vars["id"]) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error("Error un-escaping the value id: " + err.Error()) - return - } - - op := interval.NewIdExecutor(dbClient, id) - result, err := op.Execute() - if err != nil { - lc.Error(err.Error()) - switch err.(type) { - case errors.ErrIntervalNotFound: - http.Error(w, err.Error(), http.StatusNotFound) - default: - - http.Error(w, err.Error(), http.StatusInternalServerError) - } - return - } - pkg.Encode(result, w, lc) -} - -func restDeleteIntervalByID( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - scClient interfaces.SchedulerQueueClient, - intervalDeleter interval.IntervalDeleter) { - - if r.Body != nil { - defer r.Body.Close() - } - - // URL parameters - vars := mux.Vars(r) - id, err := url.QueryUnescape(vars["id"]) - - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error("Error un-escaping the value id: " + err.Error()) - return - } - - op := interval.NewDeleteByIDExecutor(intervalDeleter, scClient, id) - err = op.Execute() - - if err != nil { - handleDeleteIntervalRestErrors(err, w, lc) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -func restGetIntervalByName( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - defer r.Body.Close() - - vars := mux.Vars(r) - name, err := url.QueryUnescape(vars["name"]) - - // Issues un-escaping - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error("Error un-escaping the value name: " + err.Error()) - return - } - - op := interval.NewNameExecutor(dbClient, name) - result, err := op.Execute() - if err != nil { - switch err := err.(type) { - case errors.ErrIntervalNotFound: - http.Error(w, err.Error(), http.StatusNotFound) - case types.ErrServiceClient: - http.Error(w, err.Error(), err.StatusCode) - default: - http.Error(w, err.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - pkg.Encode(result, w, lc) - -} - -func restDeleteIntervalByName( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - scClient interfaces.SchedulerQueueClient, - intervalDeleter interval.IntervalDeleter) { - - if r.Body != nil { - defer r.Body.Close() - } - - // URL parameters - vars := mux.Vars(r) - name, err := url.QueryUnescape(vars["name"]) - - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error("Error un-escaping the value name: " + err.Error()) - return - } - - op := interval.NewDeleteByNameExecutor(intervalDeleter, scClient, name) - err = op.Execute() - - if err != nil { - handleDeleteIntervalRestErrors(err, w, lc) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) -} - -// ************************ UTILITY HANDLERS ************************************ - -func handleDeleteIntervalRestErrors(err error, w http.ResponseWriter, lc logger.LoggingClient) { - - lc.Debug(err.Error()) - switch err.(type) { - case errors.ErrIntervalNotFound: - http.Error(w, err.Error(), http.StatusNotFound) - return - case errors.ErrDbNotFound: - http.Error(w, err.Error(), http.StatusNotFound) - return - case errors.ErrIntervalStillUsedByIntervalActions: - http.Error(w, err.Error(), http.StatusBadRequest) - return - case errors.ErrIntervalNameInUse: - http.Error(w, err.Error(), http.StatusBadRequest) - return - default: - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -// Scrub all the Intervals and IntervalActions -func restScrubAllIntervals( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - defer r.Body.Close() - lc.Info("Scrubbing All Interval(s) and IntervalAction(s).") - op := interval.NewScrubExecutor(dbClient) - count, err := op.Execute() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte(strconv.Itoa(count))) - -} diff --git a/internal/support/scheduler/rest_interval_test.go b/internal/support/scheduler/rest_interval_test.go deleted file mode 100644 index 841e9487be..0000000000 --- a/internal/support/scheduler/rest_interval_test.go +++ /dev/null @@ -1,820 +0,0 @@ -/********************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 scheduler - -import ( - "bytes" - "encoding/json" - goErrors "errors" - "net/http" - "net/http/httptest" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - schedConfig "github.com/edgexfoundry/edgex-go/internal/support/scheduler/config" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/interfaces" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/interfaces/mocks" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/operators/interval" - mockDB "github.com/edgexfoundry/edgex-go/internal/support/scheduler/operators/interval/mocks" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - errors "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/types" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" -) - -// TestURI this is not really used since we are using the HTTP testing framework and not creating routes, but rather -// creating a specific handler which will accept all requests. Therefore, the URI is not important. -var TestURI = "/interval" -var TestId = "83cb038b-5a94-4707-985d-13effec62de2" -var TestName = "hourly" -var TestOtherName = "weekly" -var TestIncorrectId = "123e4567-e89b-12d3-a456-4266554400%0" -var TestIncorrectName = "hourly%b" -var TestLimit = 5 - -var intervalForAdd = contract.Interval{ - ID: TestId, - Name: TestOtherName, - Start: "20160101T000000", - End: "", - Frequency: "PT1H", -} - -var intervalForAddInvalidTime = contract.Interval{ - ID: TestId, - Name: TestOtherName, - Start: "invalid", - End: "invalid", - Frequency: "PT1H", -} - -var intervalForAddInvalidFreq = contract.Interval{ - ID: TestId, - Name: TestOtherName, - Start: "20160101T000000", - End: "", - Frequency: "PT1HS", -} - -var intervalForUpdateInvalidCron = contract.Interval{ - ID: TestId, - Name: TestOtherName, - Start: "20160101T000000", - End: "", - Frequency: "PT1H", - Cron: "invalid23", -} - -func TestGetIntervals(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createRequestIntervalAll(), - createMockIntervalLoaderAllSuccess(), - http.StatusOK, - }, - { - name: "Unexpected Error", - request: createRequestIntervalAll(), - dbMock: createMockIntervalLoaderAllErr(), - expectedStatus: http.StatusInternalServerError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetIntervals(rr, tt.request, logger.NewMockClient(), tt.dbMock, &schedConfig.ConfigurationStruct{}) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestAddInterval(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - scClient interfaces.SchedulerQueueClient - expectedStatus int - }{ - { - name: "ErrInvalidTimeFormat", - request: createRequestIntervalAdd(intervalForAddInvalidTime), - dbMock: createMockIntervalLoaderAddSuccess(), - scClient: createMockIntervalLoaderSCAddSuccess(), - expectedStatus: http.StatusBadRequest, - }, - { - name: "OK", - request: createRequestIntervalAdd(intervalForAdd), - dbMock: createMockIntervalLoaderAddSuccess(), - scClient: createMockIntervalLoaderSCAddSuccess(), - expectedStatus: http.StatusOK, - }, - { - name: "ErrIntervalNameInUse", - request: createRequestIntervalAdd(intervalForAdd), - dbMock: createMockIntervalLoaderAddNameInUse(), - scClient: createMockIntervalLoaderSCAddSuccess(), - expectedStatus: http.StatusBadRequest, - }, - - { - name: "ErrInvalidFrequencyFormat", - request: createRequestIntervalAdd(intervalForAddInvalidFreq), - dbMock: createMockIntervalLoaderAddSuccess(), - scClient: createMockIntervalLoaderSCAddSuccess(), - expectedStatus: http.StatusBadRequest, - }, - { - name: "Unexpected Error", - request: createRequestIntervalAdd(intervalForAdd), - dbMock: createMockIntervalLoaderAddErr(), - scClient: createMockIntervalLoadeSCAddErr(), - expectedStatus: http.StatusInternalServerError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restAddInterval(rr, tt.request, logger.NewMockClient(), tt.dbMock, tt.scClient) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestUpdateInterval(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - scClient interfaces.SchedulerQueueClient - expectedStatus int - }{ - { - name: "OK", - request: createRequestIntervalUpdate(intervalForAdd), - dbMock: createMockIntervalLoaderUpdateSuccess(), - scClient: createMockIntervalLoaderSCUpdateSuccess(), - expectedStatus: http.StatusOK, - }, - { - name: "ErrInvalidTimeFormat", - request: createRequestIntervalUpdate(intervalForAddInvalidTime), - dbMock: createMockIntervalLoaderUpdateSuccess(), - scClient: createMockIntervalLoaderSCUpdateSuccess(), - expectedStatus: http.StatusBadRequest, - }, - { - name: "ErrIntervalNotFound", - request: createRequestIntervalUpdate(intervalForAdd), - dbMock: createMockIntervalLoaderUpdateNotFound(), - scClient: createMockIntervalLoaderSCUpdateSuccess(), - expectedStatus: http.StatusNotFound, - }, - { - name: "ErrInvalidCronFormat", - request: createRequestIntervalUpdate(intervalForUpdateInvalidCron), - dbMock: createMockIntervalLoaderUpdateInvalidCron(), - scClient: createMockIntervalLoaderSCUpdateSuccess(), - expectedStatus: http.StatusBadRequest, - }, - { - name: "ErrInvalidFrequencyFormat", - request: createRequestIntervalUpdate(intervalForAddInvalidFreq), - dbMock: createMockIntervalLoaderUpdateSuccess(), - scClient: createMockIntervalLoaderSCUpdateSuccess(), - expectedStatus: http.StatusBadRequest, - }, - { - name: "ErrIntervalNameInUse", - request: createRequestIntervalUpdate(intervalForAdd), - dbMock: createMockIntervalLoaderUpdateNameUsed(), - scClient: createMockIntervalLoaderSCUpdateSuccess(), - expectedStatus: http.StatusBadRequest, - }, - { - name: "ErrIntervalStillUsedByIntervalActions", - request: createRequestIntervalUpdate(intervalForAdd), - dbMock: createMockIntervalLoaderUpdateNameStillUsed(), - scClient: createMockIntervalLoaderSCUpdateSuccess(), - expectedStatus: http.StatusBadRequest, - }, - { - name: "Unexpected Error", - request: createRequestIntervalUpdate(intervalForAdd), - dbMock: createMockIntervalLoaderUpdateErr(), - scClient: createMockIntervalLoadeSCUpdateErr(), - expectedStatus: http.StatusServiceUnavailable, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restUpdateInterval(rr, tt.request, logger.NewMockClient(), tt.dbMock, tt.scClient) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestIntervalById(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createRequest(ID, TestId), - createMockIntervalLoader("IntervalById", nil, TestId), - http.StatusOK, - }, - { - name: "Interval not found", - request: createRequest(ID, TestId), - dbMock: createMockIntervalLoader("IntervalById", db.ErrNotFound, TestId), - expectedStatus: http.StatusNotFound, - }, - { - name: "Error QueryUnescape", - request: createRequest(ID, TestIncorrectId), - dbMock: createMockIntervalLoader("IntervalById", nil, TestId), - expectedStatus: http.StatusBadRequest, - }, - { - name: "Other error from database", - request: createRequest(ID, TestId), - dbMock: createMockIntervalLoader("IntervalById", goErrors.New("test error"), TestId), - expectedStatus: http.StatusInternalServerError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetIntervalByID(rr, tt.request, logger.NewMockClient(), tt.dbMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func createMockIntervalLoaderAllSuccess() interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On("Intervals").Return(createIntervals(1), nil) - myMock.On("IntervalsWithLimit", TestLimit).Return(createIntervals(1), nil) - - return &myMock -} - -func createMockIntervalLoaderAllErr() interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On("Intervals").Return([]contract.Interval{}, goErrors.New("test error")) - myMock.On("IntervalsWithLimit", TestLimit).Return([]contract.Interval{}, goErrors.New("test error")) - - return &myMock -} - -func createMockIntervalLoaderAddSuccess() interfaces.DBClient { - myMock := mocks.DBClient{} - interval := createIntervals(1)[0] - - validateInterval(&intervalForAdd) - - myMock.On("IntervalByName", intervalForAdd.Name).Return(interval, nil) - myMock.On("AddInterval", intervalForAdd).Return(intervalForAdd.ID, nil) - return &myMock -} - -func createMockIntervalLoaderUpdateSuccess() interfaces.DBClient { - myMock := mocks.DBClient{} - interval := createIntervals(1)[0] - - validateInterval(&intervalForAdd) - validateInterval(&interval) - - myMock.On("IntervalById", intervalForAdd.ID).Return(interval, nil) - myMock.On("IntervalByName", intervalForAdd.Name).Return(contract.Interval{}, db.ErrNotFound) - myMock.On("IntervalActionsByIntervalName", TestName).Return([]contract.IntervalAction{}, nil) - myMock.On("UpdateInterval", intervalForAdd).Return(nil) - return &myMock -} - -func createMockIntervalLoaderUpdateNotFound() interfaces.DBClient { - myMock := mocks.DBClient{} - interval := createIntervals(1)[0] - - myMock.On("IntervalById", intervalForAdd.ID).Return(interval, goErrors.New("test error")) - myMock.On("IntervalByName", intervalForAdd.Name).Return(contract.Interval{}, db.ErrNotFound) - myMock.On("IntervalActionsByIntervalName", interval.Name).Return([]contract.IntervalAction{}, nil) - myMock.On("UpdateInterval", intervalForAdd).Return(nil) - return &myMock -} - -func createMockIntervalLoaderUpdateInvalidCron() interfaces.DBClient { - myMock := mocks.DBClient{} - interval := createIntervals(1)[0] - - myMock.On("IntervalById", intervalForUpdateInvalidCron.ID).Return(interval, nil) - myMock.On("IntervalByName", intervalForUpdateInvalidCron.Name).Return(contract.Interval{}, db.ErrNotFound) - myMock.On("IntervalActionsByIntervalName", interval.Name).Return([]contract.IntervalAction{}, nil) - myMock.On("UpdateInterval", intervalForAdd).Return(nil) - return &myMock -} - -func createMockIntervalLoaderUpdateNameUsed() interfaces.DBClient { - myMock := mocks.DBClient{} - interval := createIntervals(1)[0] - - myMock.On("IntervalById", intervalForAdd.ID).Return(interval, nil) - myMock.On("IntervalByName", intervalForAdd.Name).Return(interval, nil) - myMock.On("IntervalActionsByIntervalName", interval.Name).Return([]contract.IntervalAction{}, nil) - myMock.On("UpdateInterval", intervalForAdd).Return(nil) - return &myMock -} - -func createMockIntervalLoaderUpdateNameStillUsed() interfaces.DBClient { - myMock := mocks.DBClient{} - interval := createIntervals(1)[0] - - myMock.On("IntervalById", intervalForAdd.ID).Return(interval, nil) - myMock.On("IntervalByName", intervalForAdd.Name).Return(contract.Interval{}, db.ErrNotFound) - myMock.On("IntervalActionsByIntervalName", interval.Name).Return(createIntervalActions(1), nil) - myMock.On("UpdateInterval", intervalForAdd).Return(nil) - return &myMock -} - -func createMockIntervalLoaderAddNameInUse() interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On("IntervalByName", intervalForAdd.Name).Return(intervalForAdd, nil) - myMock.On("AddInterval", intervalForAdd).Return(intervalForAdd.ID, nil) - return &myMock -} - -func createMockIntervalLoaderAddErr() interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On("IntervalByName", intervalForAdd.Name).Return(contract.Interval{}, goErrors.New("test error")) - myMock.On("AddInterval", intervalForAdd).Return(intervalForAdd.ID, goErrors.New("test error")) - return &myMock -} - -func createMockIntervalLoaderUpdateErr() interfaces.DBClient { - myMock := mocks.DBClient{} - interval := createIntervals(1)[0] - myMock.On("IntervalById", intervalForAdd.ID).Return(interval, nil) - myMock.On("IntervalByName", intervalForAdd.Name).Return(contract.Interval{}, goErrors.New("test error")) - myMock.On("IntervalActionsByIntervalName", intervalForAdd.Name).Return([]contract.IntervalAction{}, nil) - myMock.On("UpdateInterval", intervalForAdd).Return(nil) - return &myMock -} - -// this function serves to update the unexported isValidated field, -// which can only be done by marshalling and unmarshalling to JSON. -func validateInterval(interval *contract.Interval) { - b, _ := json.Marshal(interval) - _ = interval.UnmarshalJSON(b) -} - -func createMockIntervalLoaderSCAddSuccess() interfaces.SchedulerQueueClient { - myMock := mocks.SchedulerQueueClient{} - myMock.On("AddIntervalToQueue", intervalForAdd).Return(nil) - return &myMock -} - -func createMockIntervalLoaderSCUpdateSuccess() interfaces.SchedulerQueueClient { - myMock := mocks.SchedulerQueueClient{} - myMock.On("UpdateIntervalInQueue", intervalForAdd).Return(nil) - return &myMock -} - -func createMockIntervalLoadeSCAddErr() interfaces.SchedulerQueueClient { - myMock := mocks.SchedulerQueueClient{} - myMock.On("AddIntervalToQueue", intervalForAdd).Return(nil) - return &myMock -} - -func createMockIntervalLoadeSCUpdateErr() interfaces.SchedulerQueueClient { - myMock := mocks.SchedulerQueueClient{} - myMock.On("UpdateIntervalInQueue", intervalForAdd).Return(nil) - return &myMock -} - -func createMockIntervalLoader(methodName string, desiredError error, arg string) interfaces.DBClient { - myMock := mocks.DBClient{} - - if desiredError != nil { - myMock.On(methodName, arg).Return(contract.Interval{}, desiredError) - myMock.On("IntervalById", TestId).Return(createIntervals(1)[0], desiredError) - } else { - myMock.On(methodName, arg).Return(createIntervals(1)[0], nil) - myMock.On("IntervalById", TestId).Return(createIntervals(1)[0], nil) - } - return &myMock -} - -func createMockIntervalDeleterForId(desiredError error) interfaces.DBClient { - myMock := mocks.DBClient{} - - if desiredError != nil { - myMock.On("IntervalById", TestId).Return(createIntervals(1)[0], desiredError) - myMock.On("DeleteIntervalById", TestId).Return(desiredError) - myMock.On("IntervalActionsByIntervalName", TestName).Return([]contract.IntervalAction{}, desiredError) - myMock.On("QueryIntervalByID", TestId).Return(contract.Interval{}, desiredError) - myMock.On("QueryIntervalByName", TestName).Return(createIntervals(1)[0], desiredError) - } else { - myMock.On("IntervalById", TestId).Return(createIntervals(1)[0], nil) - myMock.On("DeleteIntervalById", TestId).Return(nil) - myMock.On("IntervalActionsByIntervalName", TestName).Return([]contract.IntervalAction{}, nil) - myMock.On("QueryIntervalByID", TestId).Return(contract.Interval{}, nil) - myMock.On("QueryIntervalByName", TestName).Return(createIntervals(1)[0], nil) - } - return &myMock -} - -func createMockSCDeleterForId(interval contract.Interval, desiredError error) interfaces.SchedulerQueueClient { - myMock := mocks.SchedulerQueueClient{} - - if desiredError != nil { - myMock.On("IntervalById", TestId).Return(createIntervals(1)[0], desiredError) - myMock.On("IntervalActionsByIntervalName", TestName).Return([]contract.IntervalAction{}, desiredError) - myMock.On("QueryIntervalByID", TestId).Return(interval, desiredError) - myMock.On("RemoveIntervalInQueue", TestId).Return(desiredError) - myMock.On("QueryIntervalByName", TestName).Return(createIntervals(1)[0], desiredError) - } else { - myMock.On("IntervalById", TestId).Return(createIntervals(1)[0], nil) - myMock.On("IntervalActionsByIntervalName", TestName).Return([]contract.IntervalAction{}, nil) - myMock.On("QueryIntervalByID", TestId).Return(interval, nil) - myMock.On("RemoveIntervalInQueue", TestId).Return(nil) - myMock.On("QueryIntervalByName", TestName).Return(interval, nil) - } - return &myMock -} - -func createRequestIntervalAll() *http.Request { - req := httptest.NewRequest(http.MethodGet, TestURI, nil) - return mux.SetURLVars(req, map[string]string{}) -} - -func createRequestIntervalAdd(interval contract.Interval) *http.Request { - b, _ := json.Marshal(interval) - req := httptest.NewRequest(http.MethodPost, TestURI, bytes.NewBuffer(b)) - return mux.SetURLVars(req, map[string]string{}) -} - -func createRequestIntervalUpdate(interval contract.Interval) *http.Request { - b, _ := json.Marshal(interval) - req := httptest.NewRequest(http.MethodPut, TestURI, bytes.NewBuffer(b)) - return mux.SetURLVars(req, map[string]string{}) -} - -func createRequest(pathParamName string, pathParamValue string) *http.Request { - req := httptest.NewRequest(http.MethodGet, TestURI, nil) - return mux.SetURLVars(req, map[string]string{pathParamName: pathParamValue}) -} - -func createDeleteRequest(pathParamName string, pathParamValue string) *http.Request { - req := httptest.NewRequest(http.MethodDelete, TestURI, nil) - return mux.SetURLVars(req, map[string]string{pathParamName: pathParamValue}) -} - -func createMockIdSCDeleterSuccess() interfaces.SchedulerQueueClient { - myMock := mocks.SchedulerQueueClient{} - - myMock.On("QueryIntervalByID", TestId).Return(createIntervals(1)[0], nil) - myMock.On("RemoveIntervalInQueue", TestId).Return(nil) - myMock.On("IntervalActionsByIntervalName", TestName).Return([]contract.IntervalAction{}, nil) - return &myMock -} - -func createMockNameSCDeleterErrIntervalStillUsed() interval.IntervalDeleter { - myMock := mocks.DBClient{} - - myMock.On("DeleteIntervalById", TestId).Return(nil) - myMock.On("IntervalByName", TestName).Return(createIntervals(1)[0], nil) - myMock.On("IntervalActionsByIntervalName", TestName).Return(createIntervalActions(1), nil) - myMock.On("QueryIntervalByName", TestName).Return(createIntervals(1)[0], nil) - return &myMock -} - -func createMockIntervalDeleter(desiredError error) interval.IntervalDeleter { - dbMock := mockDB.IntervalDeleter{} - - if desiredError != nil { - dbMock.On("QueryIntervalByID", TestId).Return(contract.Interval{}, desiredError) - dbMock.On("IntervalActionsByIntervalName", TestName).Return([]contract.IntervalAction{}, desiredError) - dbMock.On("DeleteIntervalById", TestId).Return(nil) - dbMock.On("IntervalByName", TestName).Return(createIntervals(1)[0], nil) - dbMock.On("QueryIntervalByName", TestName).Return(createIntervals(1)[0], nil) - - } else { - dbMock.On("QueryIntervalByID", TestId).Return(contract.Interval{}, nil) - dbMock.On("IntervalActionsByIntervalName", TestName).Return([]contract.IntervalAction{}, nil) - dbMock.On("DeleteIntervalById", TestId).Return(nil) - dbMock.On("IntervalByName", TestName).Return(createIntervals(1)[0], nil) - dbMock.On("QueryIntervalByName", TestName).Return(createIntervals(1)[0], nil) - } - return &dbMock -} - -func createIntervals(howMany int) []contract.Interval { - var intervals []contract.Interval - for i := 0; i < howMany; i++ { - intervals = append(intervals, contract.Interval{ - ID: TestId, - Name: "hourly", - Start: "20160101T000000", - End: "", - Frequency: "PT1H", - }) - } - return intervals -} - -func createIntervalActions(howMany int) []contract.IntervalAction { - var intervals []contract.IntervalAction - for i := 0; i < howMany; i++ { - intervals = append(intervals, contract.IntervalAction{ - ID: TestId, - Name: "scrub pushed records", - Interval: "hourly", - Parameters: "", - }) - } - return intervals -} - -func TestDeleteIntervalById(t *testing.T) { - tests := []struct { - name string - idMock interval.IntervalDeleter - request *http.Request - sqClientMock interfaces.SchedulerQueueClient - expectedStatus int - }{ - { - name: "OK", - idMock: createMockIntervalDeleter(nil), - request: createDeleteRequest(ID, TestId), - sqClientMock: createMockSCDeleterForId(createIntervals(1)[0], nil), - expectedStatus: http.StatusOK, - }, - { - name: "Interval not found", - idMock: createMockIntervalDeleter(nil), - request: createDeleteRequest(ID, TestId), - sqClientMock: createMockSCDeleterForId(createIntervals(1)[0], db.ErrNotFound), - expectedStatus: http.StatusNotFound, - }, - { - name: "Error QueryUnescape", - idMock: createMockIntervalDeleter(nil), - request: createDeleteRequest(ID, TestIncorrectId), - sqClientMock: createMockSCDeleterForId(createIntervals(1)[0], nil), - expectedStatus: http.StatusBadRequest, - }, - { - name: "Unknown Error", - idMock: createMockIntervalDeleter(errors.ErrServiceClient{StatusCode: 500}), - request: createDeleteRequest(ID, TestId), - sqClientMock: createMockSCDeleterForId(createIntervals(1)[0], nil), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "ErrIntervalStillUsedByIntervalActions Error", - idMock: createMockNameSCDeleterErrIntervalStillUsed(), - request: createDeleteRequest(ID, TestId), - sqClientMock: createMockIdSCDeleterSuccess(), - expectedStatus: http.StatusBadRequest, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restDeleteIntervalByID(rr, tt.request, logger.NewMockClient(), tt.sqClientMock, tt.idMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func TestIntervalByName(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - "OK", - createRequest(NAME, TestName), - createMockIntervalLoader("IntervalByName", nil, TestName), - http.StatusOK, - }, - { - name: "Interval not found", - request: createRequest(NAME, TestName), - dbMock: createMockIntervalLoader("IntervalByName", db.ErrNotFound, TestName), - expectedStatus: http.StatusNotFound, - }, - { - name: "Error QueryUnescape", - request: createRequest(NAME, TestIncorrectName), - dbMock: createMockIntervalLoader("IntervalByName", nil, TestName), - expectedStatus: http.StatusBadRequest, - }, - { - name: "Error ErrServiceClient", - request: createRequest(NAME, TestName), - dbMock: createMockIntervalLoader("IntervalByName", errors.NewErrServiceClient(500, []byte{}), - TestName), - expectedStatus: 500, - }, - { - name: "Other error from database", - request: createRequest(NAME, TestName), - dbMock: createMockIntervalLoader("IntervalByName", goErrors.New("test error"), TestName), - expectedStatus: http.StatusInternalServerError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetIntervalByName(rr, tt.request, logger.NewMockClient(), tt.dbMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func createMockNameSCDeleterSuccess() interfaces.SchedulerQueueClient { - myMock := mocks.SchedulerQueueClient{} - - myMock.On("QueryIntervalByName", TestName).Return(createIntervals(1)[0], nil) - myMock.On("RemoveIntervalInQueue", TestId).Return(nil) - return &myMock -} - -func TestDeleteIntervalByName(t *testing.T) { - tests := []struct { - name string - idMock interval.IntervalDeleter - request *http.Request - sqClientMock interfaces.SchedulerQueueClient - expectedStatus int - }{ - { - name: "OK", - idMock: createMockIntervalDeleter(nil), - request: createDeleteRequest(NAME, TestName), - sqClientMock: createMockNameSCDeleterSuccess(), - expectedStatus: http.StatusOK, - }, - { - name: "Interval not found", - idMock: createMockIntervalDeleter(nil), - request: createDeleteRequest(NAME, TestName), - sqClientMock: createMockSCDeleterForId(createIntervals(1)[0], db.ErrNotFound), - expectedStatus: http.StatusNotFound, - }, - { - name: "Error QueryUnescape", - idMock: createMockIntervalDeleter(nil), - request: createDeleteRequest(NAME, TestIncorrectName), - sqClientMock: createMockNameSCDeleterSuccess(), - expectedStatus: http.StatusBadRequest, - }, - { - name: "Error ErrServiceClient", - idMock: createMockIntervalDeleter(errors.ErrServiceClient{StatusCode: 500}), - request: createDeleteRequest(NAME, TestName), - sqClientMock: createMockNameSCDeleterSuccess(), - expectedStatus: http.StatusInternalServerError, - }, - { - name: "ErrIntervalStillUsedByIntervalActions Error", - idMock: createMockNameSCDeleterErrIntervalStillUsed(), - request: createDeleteRequest(NAME, TestName), - sqClientMock: createMockNameSCDeleterSuccess(), - expectedStatus: http.StatusBadRequest, - }, - { - name: "Unknown Error", - idMock: createMockIntervalDeleter(errors.ErrServiceClient{StatusCode: 500}), - request: createDeleteRequest(NAME, TestName), - sqClientMock: createMockNameSCDeleterSuccess(), - expectedStatus: http.StatusInternalServerError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restDeleteIntervalByName(rr, tt.request, logger.NewMockClient(), tt.sqClientMock, tt.idMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} - -func createScrubDeleteRequest() *http.Request { - req := httptest.NewRequest(http.MethodDelete, TestURI+"/scrub/", nil) - return mux.SetURLVars(req, map[string]string{}) -} - -func createMockScrubDeleterSuccess() interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On("ScrubAllIntervals").Return(1, nil) - return &myMock -} - -func createMockScrubDeleterErr() interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On("ScrubAllIntervals").Return(0, goErrors.New("test error")) - return &myMock -} - -func TestScrubIntervals(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - - { - name: "OK", - request: createScrubDeleteRequest(), - dbMock: createMockScrubDeleterSuccess(), - expectedStatus: http.StatusOK, - }, - { - name: "Unknown Error", - request: createScrubDeleteRequest(), - dbMock: createMockScrubDeleterErr(), - expectedStatus: http.StatusInternalServerError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restScrubAllIntervals(rr, tt.request, logger.NewMockClient(), tt.dbMock) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - - return - } - }) - } -} diff --git a/internal/support/scheduler/rest_intervalaction.go b/internal/support/scheduler/rest_intervalaction.go deleted file mode 100644 index 7a8c1ad17a..0000000000 --- a/internal/support/scheduler/rest_intervalaction.go +++ /dev/null @@ -1,419 +0,0 @@ -/******************************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 scheduler - -import ( - "encoding/json" - "net/http" - "net/url" - "strconv" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - "github.com/gorilla/mux" - - "github.com/edgexfoundry/edgex-go/internal/pkg" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/config" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/errors" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/interfaces" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/operators/intervalaction" -) - -func restGetIntervalAction( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - configuration *config.ConfigurationStruct) { - - if r.Body != nil { - defer r.Body.Close() - } - op := intervalaction.NewAllExecutor(dbClient, configuration.Service) - intervalActions, err := op.Execute() - - if err != nil { - lc.Error(err.Error()) - switch err.(type) { - case errors.ErrLimitExceeded: - http.Error(w, "Exceeded max limit", http.StatusRequestEntityTooLarge) - default: - http.Error(w, err.Error(), http.StatusInternalServerError) - } - return - } - pkg.Encode(intervalActions, w, lc) -} - -func restAddIntervalAction( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - scClient interfaces.SchedulerQueueClient) { - - if r.Body != nil { - defer r.Body.Close() - } - var intervalAction contract.IntervalAction - dec := json.NewDecoder(r.Body) - err := dec.Decode(&intervalAction) - - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - lc.Info("posting new intervalAction: " + intervalAction.String()) - - op := intervalaction.NewAddExecutor(dbClient, scClient, intervalAction) - newId, err := op.Execute() - if err != nil { - switch t := err.(type) { - case errors.ErrIntervalActionNameInUse: - http.Error(w, t.Error(), http.StatusBadRequest) - case errors.ErrIntervalNotFound: - http.Error(w, t.Error(), http.StatusBadRequest) - default: - http.Error(w, t.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte(newId)) -} - -/* -Handler for the IntervalAction API -Status code 400 - bad request, malformed or missing data -Status code 404 - interval not found -Status code 413 - number of interval actions exceeds limit -Status code 500 - unanticipated issues -api/v1/interval -*/ -func intervalActionHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - scClient interfaces.SchedulerQueueClient, - configuration *config.ConfigurationStruct) { - - if r.Body != nil { - defer r.Body.Close() - } - - switch r.Method { - case http.MethodGet: - intervalActions, err := getIntervalActions(configuration.Service.MaxResultCount, dbClient) - if err != nil { - lc.Error(err.Error()) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - pkg.Encode(intervalActions, w, lc) - break - // Post a new IntervalAction - case http.MethodPost: - var intervalAction models.IntervalAction - dec := json.NewDecoder(r.Body) - err := dec.Decode(&intervalAction) - - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error("error decoding intervalAction" + err.Error()) - return - } - lc.Info("posting new intervalAction: " + intervalAction.String()) - - newId, err := addNewIntervalAction(intervalAction, dbClient, scClient) - if err != nil { - switch t := err.(type) { - case errors.ErrIntervalActionNameInUse: - http.Error(w, t.Error(), http.StatusBadRequest) - case errors.ErrInvalidTimeFormat: - http.Error(w, t.Error(), http.StatusBadRequest) - case errors.ErrInvalidFrequencyFormat: - http.Error(w, t.Error(), http.StatusBadRequest) - default: - http.Error(w, t.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte(newId)) - break - case http.MethodPut: - var from models.IntervalAction - dec := json.NewDecoder(r.Body) - err := dec.Decode(&from) - - // Problem decoding - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error("Error decoding the intervalAction: " + err.Error()) - return - } - - lc.Info("Updating IntervalAction: " + from.ID) - err = updateIntervalAction(from, dbClient, scClient) - if err != nil { - switch t := err.(type) { - case errors.ErrIntervalNotFound: - http.Error(w, t.Error(), http.StatusNotFound) - case errors.ErrInvalidCronFormat: - http.Error(w, t.Error(), http.StatusBadRequest) - case errors.ErrInvalidFrequencyFormat: - http.Error(w, t.Error(), http.StatusBadRequest) - case errors.ErrInvalidTimeFormat: - http.Error(w, t.Error(), http.StatusBadRequest) - case errors.ErrIntervalStillUsedByIntervalActions: - http.Error(w, t.Error(), http.StatusBadRequest) - case errors.ErrIntervalNameInUse: - http.Error(w, t.Error(), http.StatusBadRequest) - default: - http.Error(w, err.Error(), http.StatusServiceUnavailable) - } - lc.Error(err.Error()) - return - } - - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) - } -} - -/* -Handler for the IntervalAction By-ID API -Status code 404 - interval not found -Status code 500 - unanticipated issues -api/v1/interval -*/ -func intervalActionByIdHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - scClient interfaces.SchedulerQueueClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - // URL parameters - vars := mux.Vars(r) - id, err := url.QueryUnescape(vars["id"]) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error("Error un-escaping the value interval id: " + err.Error()) - return - } - - switch r.Method { - case http.MethodGet: - intervalAction, err := getIntervalActionById(id, dbClient) - if err != nil { - switch x := err.(type) { - case errors.ErrIntervalActionNotFound: - http.Error(w, x.Error(), http.StatusNotFound) - default: - http.Error(w, x.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - pkg.Encode(intervalAction, w, lc) - // Post a new Interval Action - case http.MethodDelete: - if err = deleteIntervalActionById(id, dbClient, scClient); err != nil { - switch err.(type) { - case errors.ErrIntervalActionNotFound: - http.Error(w, err.Error(), http.StatusBadRequest) - return - default: - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) - } -} - -/* -Handler for the IntervalAction By-Name API -Status code 404 - interval action not found -Status code 500 - unanticipated issues -api/v1/interval -*/ -func intervalActionByNameHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient, - scClient interfaces.SchedulerQueueClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - // URL parameters - vars := mux.Vars(r) - name, err := url.QueryUnescape(vars["name"]) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error("Error un-escaping the value name: " + err.Error()) - return - } - - switch r.Method { - case http.MethodGet: - intervalAction, err := getIntervalActionByName(name, dbClient) - if err != nil { - switch x := err.(type) { - case errors.ErrIntervalActionNotFound: - http.Error(w, x.Error(), http.StatusNotFound) - default: - http.Error(w, x.Error(), http.StatusInternalServerError) - } - lc.Error(err.Error()) - return - } - pkg.Encode(intervalAction, w, lc) - // Post a new Interval Action - case http.MethodDelete: - if err = deleteIntervalActionByName(name, dbClient, scClient); err != nil { - switch err.(type) { - case errors.ErrIntervalActionNotFound: - http.Error(w, err.Error(), http.StatusBadRequest) - return - default: - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte("true")) - } -} - -/* -Handler for the IntervalAction By-Target API -Status code 404 - interval action not found -Status code 500 - unanticipated issues -api/v1/interval -*/ -func intervalActionByTargetHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - if r.Body != nil { - defer r.Body.Close() - } - - // URL parameters - vars := mux.Vars(r) - target, err := url.QueryUnescape(vars["target"]) - - // Issues un-escaping - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error("Error un-escaping the value descriptor name: " + err.Error()) - return - } - - switch r.Method { - case http.MethodGet: - intervalActions, err := getIntervalActionsByTarget(target, dbClient) - if err != nil { - lc.Error(err.Error()) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - pkg.Encode(intervalActions, w, lc) - break - } -} - -/* -Handler for the IntervalAction By-Interval API -Status code 404 - interval action not found -Status code 500 - unanticipated issues -api/v1/interval -*/ -func intervalActionByIntervalHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - if r.Body != nil { - defer r.Body.Close() - } - - // URL parameters - vars := mux.Vars(r) - interval, err := url.QueryUnescape(vars["interval"]) - - // Issues un-escaping - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - lc.Error("Error un-escaping the value interval name: " + err.Error()) - return - } - - switch r.Method { - case http.MethodGet: - intervalActions, err := getIntervalActionsByInterval(interval, dbClient) - if err != nil { - lc.Error(err.Error()) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - pkg.Encode(intervalActions, w, lc) - break - } -} - -// Scrub only the IntervalAction(s) leaving the Interval(s) behind -func scrubIntervalActionsHandler( - w http.ResponseWriter, - r *http.Request, - lc logger.LoggingClient, - dbClient interfaces.DBClient) { - - defer r.Body.Close() - - switch r.Method { - case http.MethodDelete: - count, err := scrubAllInteralActions(lc, dbClient) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - w.Header().Set(clients.ContentType, clients.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - w.Write([]byte(strconv.Itoa(count))) - } -} diff --git a/internal/support/scheduler/rest_intervalaction_test.go b/internal/support/scheduler/rest_intervalaction_test.go deleted file mode 100644 index 38afc1a037..0000000000 --- a/internal/support/scheduler/rest_intervalaction_test.go +++ /dev/null @@ -1,251 +0,0 @@ -/********************************************************************* - * Copyright 2019 VMware Inc. - * - * 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 scheduler - -import ( - "bytes" - "encoding/json" - "errors" - "net/http" - "net/http/httptest" - "testing" - - "github.com/edgexfoundry/edgex-go/internal/pkg/db" - schedConfig "github.com/edgexfoundry/edgex-go/internal/support/scheduler/config" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/interfaces" - "github.com/edgexfoundry/edgex-go/internal/support/scheduler/interfaces/mocks" - - bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v2/config" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" - - "github.com/gorilla/mux" -) - -var intervalActionForAdd = contract.IntervalAction{ - ID: TestId, - Name: "scrub pushed records for add", - Interval: "hourly", - Parameters: "", - Target: "test target", -} -var otherIntervalActionForAdd = contract.IntervalAction{ - ID: TestId, - Name: "scrub pushed records for add 2", - Interval: "hourly", - Parameters: "", - Target: "test target", -} -var InvalidIntervalActionForAdd = contract.IntervalAction{ - ID: TestId, - Name: "scrub pushed records for add 2", - Interval: "hourly", - Parameters: "", -} - -var TestIntervalActionURI = "/" + INTERVALACTION - -func TestGetIntervalAction(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - expectedStatus int - }{ - { - name: "OK", - request: createRequestIntervalAction(), - dbMock: createMockIntervalActionLoaderAllSuccess(), - expectedStatus: http.StatusOK, - }, - { - name: "Exceeded max limit", - request: createRequestIntervalAction(), - dbMock: createMockIntervalActionLoaderAllExceedErr(), - expectedStatus: http.StatusRequestEntityTooLarge, - }, - { - name: "Unexpected Error", - request: createRequestIntervalAction(), - dbMock: createMockIntervalActionLoaderAllErr(), - expectedStatus: http.StatusInternalServerError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restGetIntervalAction( - rr, - tt.request, - logger.NewMockClient(), - tt.dbMock, - &schedConfig.ConfigurationStruct{Service: bootstrapConfig.ServiceInfo{MaxResultCount: 1}}) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func TestAddIntervalAction(t *testing.T) { - tests := []struct { - name string - request *http.Request - dbMock interfaces.DBClient - scClient interfaces.SchedulerQueueClient - expectedStatus int - }{ - { - name: "Error IntervalActionNameInUes", - request: createRequestIntervalActionAdd(intervalActionForAdd), - dbMock: createMockIntervalActionLoaderAddNameInUse(), - scClient: createMockIntervalActionLoaderSCAddSuccess(), - expectedStatus: http.StatusBadRequest, - }, - { - name: "OK", - request: createRequestIntervalActionAdd(intervalActionForAdd), - dbMock: createMockIntervalActionLoaderAddSuccess(), - scClient: createMockIntervalActionLoaderSCAddSuccess(), - expectedStatus: http.StatusOK, - }, - { - name: "Error Decoding", - request: createRequestIntervalActionAdd(InvalidIntervalActionForAdd), - dbMock: createMockIntervalActionLoaderAddSuccess(), - scClient: createMockIntervalActionLoaderSCAddSuccess(), - expectedStatus: http.StatusBadRequest, - }, - { - name: "Error IntervalActionNameInUes", - request: createRequestIntervalActionAdd(intervalActionForAdd), - dbMock: createMockIntervalActionLoaderAddNameInUse(), - scClient: createMockIntervalActionLoaderSCAddSuccess(), - expectedStatus: http.StatusBadRequest, - }, - { - name: "Error IntervalNotFound", - request: createRequestIntervalActionAdd(intervalActionForAdd), - dbMock: createMockIntervalActionLoaderAddIntervalNotFoundErr(), - scClient: createMockIntervalActionLoaderSCAddSuccess(), - expectedStatus: http.StatusBadRequest, - }, - { - name: "Error Unknown", - request: createRequestIntervalActionAdd(intervalActionForAdd), - dbMock: createMockIntervalActionLoaderAddErr(), - scClient: createMockIntervalActionLoaderSCAddSuccess(), - expectedStatus: http.StatusInternalServerError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rr := httptest.NewRecorder() - restAddIntervalAction(rr, tt.request, logger.NewMockClient(), tt.dbMock, tt.scClient) - response := rr.Result() - if response.StatusCode != tt.expectedStatus { - t.Errorf("status code mismatch -- expected %v got %v", tt.expectedStatus, response.StatusCode) - return - } - }) - } -} - -func createMockIntervalActionLoaderAllSuccess() interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On("IntervalActions").Return(createIntervalActions(1), nil) - return &myMock -} - -func createMockIntervalActionLoaderAllExceedErr() interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On("IntervalActions").Return(createIntervalActions(200), nil) - return &myMock -} - -func createMockIntervalActionLoaderAllErr() interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On("IntervalActions").Return([]contract.IntervalAction{}, errors.New("test error")) - return &myMock -} - -func createMockIntervalActionLoaderAddSuccess() interfaces.DBClient { - myMock := mocks.DBClient{} - intervalAction := createIntervalActions(1)[0] - b, _ := json.Marshal(intervalAction) - _ = intervalAction.UnmarshalJSON(b) - validateIntervalAction(&intervalActionForAdd) - - myMock.On("IntervalActionByName", intervalActionForAdd.Name).Return(intervalAction, nil) - myMock.On("IntervalByName", intervalActionForAdd.Interval).Return(createIntervals(1)[0], nil) - myMock.On("AddIntervalAction", intervalActionForAdd).Return(intervalActionForAdd.ID, nil) - return &myMock -} - -func createMockIntervalActionLoaderAddNameInUse() interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On("IntervalActionByName", intervalActionForAdd.Name).Return(intervalActionForAdd, nil) - return &myMock -} - -func createMockIntervalActionLoaderAddIntervalNotFoundErr() interfaces.DBClient { - myMock := mocks.DBClient{} - myMock.On("IntervalActionByName", intervalActionForAdd.Name).Return(otherIntervalActionForAdd, nil) - myMock.On("IntervalByName", intervalActionForAdd.Interval).Return(contract.Interval{}, db.ErrNotFound) - return &myMock -} - -func createMockIntervalActionLoaderAddErr() interfaces.DBClient { - myMock := mocks.DBClient{} - intervalAction := createIntervalActions(1)[0] - b, _ := json.Marshal(intervalAction) - _ = intervalAction.UnmarshalJSON(b) - validateIntervalAction(&intervalActionForAdd) - - myMock.On("IntervalActionByName", intervalActionForAdd.Name).Return(intervalAction, nil) - myMock.On("IntervalByName", intervalActionForAdd.Interval).Return(createIntervals(1)[0], nil) - myMock.On("AddIntervalAction", intervalActionForAdd).Return(intervalActionForAdd.ID, errors.New("test error")) - return &myMock -} - -func validateIntervalAction(intervalAction *contract.IntervalAction) { - b, _ := json.Marshal(intervalAction) - _ = intervalAction.UnmarshalJSON(b) -} - -func createMockIntervalActionLoaderSCAddSuccess() interfaces.SchedulerQueueClient { - myMock := mocks.SchedulerQueueClient{} - validateIntervalAction(&otherIntervalActionForAdd) - myMock.On("QueryIntervalActionByName", intervalActionForAdd.Name).Return(otherIntervalActionForAdd, nil) - myMock.On("AddIntervalActionToQueue", intervalActionForAdd).Return(nil) - - return &myMock -} - -func createRequestIntervalAction() *http.Request { - req := httptest.NewRequest(http.MethodGet, TestIntervalActionURI, nil) - return mux.SetURLVars(req, map[string]string{}) -} - -func createRequestIntervalActionAdd(intervalAction contract.IntervalAction) *http.Request { - b, _ := json.Marshal(intervalAction) - req := httptest.NewRequest(http.MethodPost, TestIntervalActionURI, bytes.NewBuffer(b)) - return mux.SetURLVars(req, map[string]string{}) -} diff --git a/internal/support/scheduler/router.go b/internal/support/scheduler/router.go deleted file mode 100644 index 18353838cd..0000000000 --- a/internal/support/scheduler/router.go +++ /dev/null @@ -1,225 +0,0 @@ -/******************************************************************************* - * Copyright 2018 Dell Inc. - * - * 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 scheduler - -import ( - "net/http" - - "github.com/edgexfoundry/edgex-go/internal/pkg" - "github.com/edgexfoundry/edgex-go/internal/pkg/bootstrap/container" - "github.com/edgexfoundry/edgex-go/internal/pkg/correlation" - "github.com/edgexfoundry/edgex-go/internal/pkg/telemetry" - schedulerContainer "github.com/edgexfoundry/edgex-go/internal/support/scheduler/container" - - bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/container" - "github.com/edgexfoundry/go-mod-bootstrap/v2/di" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" - - "github.com/gorilla/mux" -) - -func loadRestRoutes(r *mux.Router, dic *di.Container) { - // Ping Resource - r.HandleFunc(clients. - ApiPingRoute, - func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set(clients.ContentType, clients.ContentTypeText) - _, _ = w.Write([]byte("pong")) - }).Methods(http.MethodGet) - - // Configuration - r.HandleFunc(clients. - ApiConfigRoute, - func(w http.ResponseWriter, _ *http.Request) { - pkg.Encode(schedulerContainer.ConfigurationFrom(dic.Get), w, bootstrapContainer.LoggingClientFrom(dic.Get)) - }).Methods(http.MethodGet) - - // Metrics - r.HandleFunc(clients. - ApiMetricsRoute, - func(w http.ResponseWriter, _ *http.Request) { - pkg.Encode(telemetry.NewSystemUsage(), w, bootstrapContainer.LoggingClientFrom(dic.Get)) - }).Methods(http.MethodGet) - - // Version - r.HandleFunc(clients.ApiVersionRoute, pkg.VersionHandler).Methods(http.MethodGet) - - // Interval - r.HandleFunc(clients. - ApiIntervalRoute, - func(w http.ResponseWriter, r *http.Request) { - restGetIntervals( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - schedulerContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - r.HandleFunc(clients. - ApiIntervalRoute, - func(w http.ResponseWriter, r *http.Request) { - restUpdateInterval( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - schedulerContainer.QueueFrom(dic.Get)) - }).Methods(http.MethodPut) - r.HandleFunc(clients. - ApiIntervalRoute, - func(w http.ResponseWriter, r *http.Request) { - restAddInterval( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - schedulerContainer.QueueFrom(dic.Get)) - }).Methods(http.MethodPost) - interval := r.PathPrefix(clients.ApiIntervalRoute).Subrouter() - interval.HandleFunc( - "/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetIntervalByID( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - interval.HandleFunc( - "/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteIntervalByID(w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - schedulerContainer.QueueFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodDelete) - interval.HandleFunc( - "/"+NAME+"/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restGetIntervalByName( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - interval.HandleFunc( - "/"+NAME+"/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - restDeleteIntervalByName( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - schedulerContainer.QueueFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodDelete) - // Scrub "Intervals and IntervalActions" - interval.HandleFunc( - "/"+SCRUB+"/", - func(w http.ResponseWriter, r *http.Request) { - restScrubAllIntervals( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodDelete) - - // IntervalAction - r.HandleFunc(clients. - ApiIntervalActionRoute, - func(w http.ResponseWriter, r *http.Request) { - restGetIntervalAction( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - schedulerContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodGet) - r.HandleFunc(clients. - ApiIntervalActionRoute, - func(w http.ResponseWriter, r *http.Request) { - restAddIntervalAction( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - schedulerContainer.QueueFrom(dic.Get)) - }).Methods(http.MethodPost) - r.HandleFunc(clients. - ApiIntervalActionRoute, - func(w http.ResponseWriter, r *http.Request) { - intervalActionHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - schedulerContainer.QueueFrom(dic.Get), - schedulerContainer.ConfigurationFrom(dic.Get)) - }).Methods(http.MethodPut) - intervalAction := r.PathPrefix(clients.ApiIntervalActionRoute).Subrouter() - intervalAction.HandleFunc( - "/{"+ID+"}", - func(w http.ResponseWriter, r *http.Request) { - intervalActionByIdHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - schedulerContainer.QueueFrom(dic.Get)) - }).Methods(http.MethodGet, http.MethodDelete) - intervalAction.HandleFunc( - "/"+NAME+"/{"+NAME+"}", - func(w http.ResponseWriter, r *http.Request) { - intervalActionByNameHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get), - schedulerContainer.QueueFrom(dic.Get)) - }).Methods(http.MethodGet, http.MethodDelete) - intervalAction.HandleFunc( - "/"+TARGET+"/{"+TARGET+"}", - func(w http.ResponseWriter, r *http.Request) { - intervalActionByTargetHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - intervalAction.HandleFunc( - "/"+INTERVAL+"/{"+INTERVAL+"}", - func(w http.ResponseWriter, r *http.Request) { - intervalActionByIntervalHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodGet) - - // Scrub "IntervalActions" - intervalAction.HandleFunc( - "/"+SCRUB+"/", - func(w http.ResponseWriter, r *http.Request) { - scrubIntervalActionsHandler( - w, - r, - bootstrapContainer.LoggingClientFrom(dic.Get), - container.DBClientFrom(dic.Get)) - }).Methods(http.MethodDelete) - - r.Use(correlation.ManageHeader) - r.Use(correlation.LoggingMiddleware(bootstrapContainer.LoggingClientFrom(dic.Get))) -} diff --git a/internal/support/scheduler/schedule.go b/internal/support/scheduler/schedule.go index cffeadd9fb..c28a63a955 100644 --- a/internal/support/scheduler/schedule.go +++ b/internal/support/scheduler/schedule.go @@ -18,6 +18,7 @@ import ( "sync" "time" + "github.com/edgexfoundry/go-mod-core-contracts/v2/clients" "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" contract "github.com/edgexfoundry/go-mod-core-contracts/v2/models" queueV1 "gopkg.in/eapache/queue.v1" @@ -25,6 +26,10 @@ import ( "github.com/edgexfoundry/edgex-go/internal/support/scheduler/config" ) +const ( + ContentLengthKey = "Content-Length" +) + // the interval specific shared variables var ( mutex sync.Mutex @@ -49,26 +54,6 @@ func StopTicker(ticker *time.Ticker) { ticker.Stop() } -// utility function -func clearQueue() { - mutex.Lock() - defer mutex.Unlock() - - for intervalQueue.Length() > 0 { - intervalQueue.Remove() - } -} - -func clearMaps() { - intervalIdToContextMap = make(map[string]*IntervalContext) // map : interval id -> interval context - intervalNameToContextMap = make(map[string]*IntervalContext) // map : interval name -> interval context - intervalNameToIdMap = make(map[string]string) // map : interval name -> interval id - intervalActionIdToIntervalMap = make(map[string]string) // map : interval action id -> interval id - intervalActionNameToIntervalMap = make(map[string]string) // map : interval action name -> interval id - intervalActionNameToIntervalActionIdMap = make(map[string]string) // map : interval action name -> interval actionId - -} - func addIntervalOperation(interval contract.Interval, context *IntervalContext) { intervalIdToContextMap[interval.ID] = context intervalNameToContextMap[interval.Name] = context @@ -97,11 +82,6 @@ type QueueClient struct { } // NewClient -func NewSchedulerQueueClient(lc logger.LoggingClient) *QueueClient { - return &QueueClient{ - loggingClient: lc, - } -} func (qc *QueueClient) Connect() (string, error) { return "alive..", nil @@ -555,7 +535,7 @@ func getHttpRequest( return nil, err } - req.Header.Set(ContentTypeKey, ContentTypeJsonValue) + req.Header.Set(clients.ContentType, clients.ContentTypeJSON) if len(params) > 0 { req.Header.Set(ContentLengthKey, strconv.Itoa(len(params))) diff --git a/internal/support/scheduler/schedulecontext.go b/internal/support/scheduler/schedulecontext.go index e7f1a45586..465a1415f0 100644 --- a/internal/support/scheduler/schedulecontext.go +++ b/internal/support/scheduler/schedulecontext.go @@ -14,6 +14,10 @@ import ( "github.com/edgexfoundry/go-mod-core-contracts/v2/models" ) +const ( + SchedulerTimeFormat = "20060102T150405" +) + type IntervalContext struct { Interval models.Interval IntervalActionsMap map[string]models.IntervalAction @@ -46,7 +50,7 @@ func (sc *IntervalContext) Reset(interval models.Interval, lc logger.LoggingClie if sc.Interval.Start == "" { sc.StartTime = time.Now() } else { - t, err := time.Parse(TIMELAYOUT, sc.Interval.Start) + t, err := time.Parse(SchedulerTimeFormat, sc.Interval.Start) if err != nil { lc.Error("parse time error, the original time string is : " + sc.Interval.Start) } @@ -58,7 +62,7 @@ func (sc *IntervalContext) Reset(interval models.Interval, lc logger.LoggingClie // use max time sc.EndTime = time.Unix(1<<63-62135596801, 999999999) } else { - t, err := time.Parse(TIMELAYOUT, sc.Interval.End) + t, err := time.Parse(SchedulerTimeFormat, sc.Interval.End) if err != nil { lc.Error("parse time error, the original time string is : " + sc.Interval.End) } diff --git a/internal/support/scheduler/schedulercontext_test.go b/internal/support/scheduler/schedulercontext_test.go deleted file mode 100644 index d2d0a71cd0..0000000000 --- a/internal/support/scheduler/schedulercontext_test.go +++ /dev/null @@ -1,250 +0,0 @@ -// -// Copyright (c) 2018 Tencent -// -// Copyright (c) 2017 Dell Inc -// -// SPDX-License-Identifier: Apache-2.0 - -package scheduler - -import ( - "testing" - "time" - - "github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v2/models" -) - -// Test common const -const ( - TestUnexpectedMsg = "unexpected result" - TestUnexpectedMsgFormatStr = "unexpected result, active: '%s' but expected: '%s'" - TestUnexpectedMsgFormatStrForIntVal = "unexpected result, active: '%d' but expected: '%d'" - TestUnexpectedMsgFormatStrForFloatVal = "unexpected result, active: '%f' but expected: '%f'" - TestUnexpectedMsgFormatStrForFloatExp = "unexpected result, active: '%s' but expected: '%f'" - TestUnexpectedMsgFormatStrForBoolVal = "unexpected result, active: '%t' but expected: '%t'" - TestUnexpectedMsgFormatStrForInt64Val = "unexpected result, active: '%d' but expected: '%d'" - TestUnexpectedMsgFormatStrForInt64Exp = "unexpected result, active: '%s' but expected: '%d'" -) - -// Test Schedule model const fields -const ( - TestIntervalName = "midnight-1" - TestIntervalStart = "20000101T000000" - TestIntervalEnd = "" - TestIntervalFrequency = "24h" - TestIntervalCron = "This is a description" - TestIntervalRunOnce = true - TestIntervalUpdatingName = "midnight-2" - TestBadFrequency = "423" -) - -// Test IntervalAction model const fields -const ( - TestIntervalActionEventId = "testIntervalActionEventId" - TestIntervalActionName = "pushed events" - TestIntervalActionParameters = "" - TestIntervalActionService = "notifications" - TestIntervalActionSchedule = TestIntervalName - TestIntervalActionAddressableName = "MQTT" - TestIntervalActionAddressableProtocol = "MQTT" - TestIntervalActionUpdatingName = "pushed events-1" -) - -func TestRet(t *testing.T) { - testInterval := models.Interval{ - Name: TestIntervalName, - Start: TestIntervalStart, - End: TestIntervalEnd, - Frequency: TestIntervalFrequency, - Cron: TestIntervalCron, - RunOnce: TestIntervalRunOnce, - } - - lc := logger.NewMockClient() - - // init reset - testIntervalContext := IntervalContext{} - testIntervalContext.Reset(testInterval, lc) - - if testInterval.Name != testIntervalContext.Interval.Name { - t.Fatalf(TestUnexpectedMsgFormatStr, testIntervalContext.Interval.Name, testInterval.Name) - } - - if testIntervalContext.MaxIterations != 1 { - t.Fatalf(TestUnexpectedMsgFormatStrForIntVal, testIntervalContext.MaxIterations, 1) - } - - // run times, current and max iteration - testInterval.RunOnce = false - testIntervalContext.Reset(testInterval, lc) - - if testIntervalContext.MaxIterations != 0 { - t.Fatalf(TestUnexpectedMsgFormatStrForIntVal, testIntervalContext.MaxIterations, 0) - } - - if testIntervalContext.CurrentIterations != 0 { - t.Fatalf(TestUnexpectedMsgFormatStrForIntVal, testIntervalContext.CurrentIterations, 0) - } - - // start time - if testIntervalContext.StartTime == (time.Time{}) { - t.Fatalf(TestUnexpectedMsg) - } - - testInterval.Start = "20180101T010101" - testIntervalContext.Reset(testInterval, lc) - - if testIntervalContext.StartTime.Year() != 2018 { - t.Fatalf(TestUnexpectedMsgFormatStrForIntVal, testIntervalContext.StartTime.Year(), 2018) - } - - // end time - if testIntervalContext.EndTime == (time.Time{}) { - t.Error(TestUnexpectedMsg) - } - - testInterval.End = "20170101T010101" - testIntervalContext.Reset(testInterval, lc) - - if testIntervalContext.EndTime.Year() != 2017 { - t.Fatalf(TestUnexpectedMsgFormatStrForIntVal, testIntervalContext.EndTime.Year(), 2017) - } - - // frequency - if testIntervalContext.Frequency.Hours() != 24 { - t.Fatalf(TestUnexpectedMsgFormatStrForFloatVal, testIntervalContext.Frequency.Hours(), 24.0) - } - - testInterval.Frequency = "60s" - testIntervalContext.Reset(testInterval, lc) - if testIntervalContext.Frequency.Seconds() != 60 { - t.Fatalf(TestUnexpectedMsgFormatStrForFloatVal, testIntervalContext.Frequency.Seconds(), 60.0) - } - - // re-init time - testInterval.Start = "" - testInterval.End = "" - testInterval.RunOnce = true - - testIntervalContext.Reset(testInterval, lc) - - // next time - if testIntervalContext.StartTime != testIntervalContext.NextTime { - t.Error(TestUnexpectedMsg) - } - - if testIntervalContext.NextTime.Unix() > time.Now().Unix() { - t.Error(TestUnexpectedMsg) - } - - testInterval.RunOnce = false - testIntervalContext.Reset(testInterval, lc) - - if testIntervalContext.StartTime.Unix() > testIntervalContext.NextTime.Unix() { - t.Error(TestUnexpectedMsg) - } - - testInterval.Start = "20180101T010101" - testInterval.Frequency = "24h" - testIntervalContext.Reset(testInterval, lc) - - if testIntervalContext.StartTime.Unix() > testIntervalContext.NextTime.Unix() { - t.Error(TestUnexpectedMsg) - } - - if testIntervalContext.NextTime.Unix() < time.Now().Unix() { - t.Fatalf(TestUnexpectedMsg) - } - -} - -func TestIsComplete(t *testing.T) { - testInterval := models.Interval{ - Name: TestIntervalName, - Start: TestIntervalStart, - End: TestIntervalEnd, - Frequency: TestIntervalFrequency, - Cron: TestIntervalCron, - RunOnce: true, - } - - lc := logger.NewMockClient() - - // init reset - testIntervalContext := IntervalContext{} - testIntervalContext.Reset(testInterval, lc) - - if !testIntervalContext.IsComplete() { - t.Fatalf(TestUnexpectedMsgFormatStrForBoolVal, testIntervalContext.IsComplete(), true) - } - - testInterval.Start = "20180101T010101" - testInterval.Frequency = "24h" - testInterval.RunOnce = false - testIntervalContext.Reset(testInterval, lc) - - if testIntervalContext.IsComplete() { - t.Fatalf(TestUnexpectedMsgFormatStrForBoolVal, testIntervalContext.IsComplete(), false) - } -} - -func TestParseNanoSecondFrequency(t *testing.T) { - - durationStr := "50ns" - duration, err := parseFrequency(durationStr) - if err != nil { - t.Fatalf(TestUnexpectedMsgFormatStrForInt64Exp, durationStr, 50) - } - if duration.Nanoseconds() != int64(50) { - t.Fatalf(TestUnexpectedMsgFormatStrForInt64Val, duration.Nanoseconds(), 50) - } -} - -// Note Time.Duration does not support milliseconds, or microseconds directly. -func TestParseMicrosecondsFrequency(t *testing.T) { - durationStr := "1us" - duration, err := parseFrequency(durationStr) - if err != nil { - t.Fatalf(TestUnexpectedMsgFormatStrForInt64Exp, durationStr, 1000) - } - if duration.Nanoseconds() != int64(1000) { - t.Fatalf(TestUnexpectedMsgFormatStrForInt64Val, duration.Nanoseconds(), 1000) - } -} - -// Note Time.Duration does not support milliseconds, or microseconds directly. -func TestParseMillisecondFrequency(t *testing.T) { - - durationStr := "500ms" - duration, err := parseFrequency(durationStr) - if err != nil { - t.Fatalf(TestUnexpectedMsgFormatStrForFloatExp, durationStr, .5) - } - - if duration.Seconds() != .5 { - t.Fatalf(TestUnexpectedMsgFormatStrForFloatVal, duration.Seconds(), .5) - } -} - -func TestParseFrequency(t *testing.T) { - durationStr := "24h" - duration, err := parseFrequency(durationStr) - if err != nil { - t.Fatalf(TestUnexpectedMsgFormatStrForFloatExp, durationStr, 24.0) - } - if duration.Hours() != 24 { - t.Fatalf(TestUnexpectedMsgFormatStrForFloatVal, duration.Hours(), 24.0) - } - - durationStr = "50s" - duration, err = parseFrequency(durationStr) - - if err != nil { - t.Fatalf(TestUnexpectedMsgFormatStrForFloatExp, durationStr, 24.0) - } - - if duration.Seconds() != 50 { - t.Fatalf(TestUnexpectedMsgFormatStrForFloatVal, duration.Seconds(), 50.0) - } -} diff --git a/internal/support/scheduler/utils.go b/internal/support/scheduler/utils.go index 34c8c4d510..a8a59b3027 100644 --- a/internal/support/scheduler/utils.go +++ b/internal/support/scheduler/utils.go @@ -23,21 +23,6 @@ const ( frequencyPattern = `^P(\d+Y)?(\d+M)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?$` ) -// Convert millisecond string to Time -func msToTime(ms string) (time.Time, error) { - msInt, err := strconv.ParseInt(ms, 10, 64) - if err != nil { - // todo: support-scheduler will be removed later issue_650a - t, err := time.Parse(TIMELAYOUT, ms) - if err == nil { - return t, nil - } - return time.Time{}, err - } - - return time.Unix(0, msInt*int64(time.Millisecond)), nil -} - // Frequency indicates how often the specific resource needs to be polled. // It represents as a duration string. Will not do days you must compute to hours // diff --git a/internal/system/agent/main.go b/internal/system/agent/main.go index 03668acda1..02f46775db 100644 --- a/internal/system/agent/main.go +++ b/internal/system/agent/main.go @@ -35,7 +35,7 @@ import ( "github.com/gorilla/mux" ) -func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router, readyStream chan<- bool) { +func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router) { startupTimer := startup.NewStartUpTimer(clients.SystemManagementAgentServiceKey) // All common command-line flags have been moved to DefaultCommonFlags. Service specific flags can be add here, @@ -71,6 +71,5 @@ func Main(ctx context.Context, cancel context.CancelFunc, router *mux.Router, re NewBootstrap(router).BootstrapHandler, httpServer.BootstrapHandler, handlers.NewStartMessage(clients.SystemManagementAgentServiceKey, edgex.Version).BootstrapHandler, - handlers.NewReady(httpServer, readyStream).BootstrapHandler, }) } diff --git a/openapi/v1/core-command.yaml b/openapi/v1/core-command.yaml deleted file mode 100644 index 622c10f73e..0000000000 --- a/openapi/v1/core-command.yaml +++ /dev/null @@ -1,376 +0,0 @@ -openapi: 3.0.0 -info: - title: core-command - version: 1.2.1 -servers: -- url: http://localhost:48082/api -paths: - /v1/config: - get: - description: Fetch the current state of the service's configuration. - responses: - 200: - description: The service's configuration as JSON document - 400: - description: Request is invalid or unparseable or if the - underlying configuration cannot be serialized to JSON properly. - /v1/device: - get: - description: Retrieve a list of all devices and their available commands. - responses: - 200: - description: List of CommandResponse containing the devices and their commands. - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/commandresponse' - 500: - description: For unanticipated or unknown issues encountered. - /v1/device/name/{name}: - get: - description: Retrieve a device by name and its available commands. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: CommandResponse containing the device and its commands. - content: - application/json: - schema: - $ref: '#/components/schemas/commandresponse' - 404: - description: If no device exists by the name provided. - 500: - description: For unanticipated or unknown issues encountered. - /v1/device/name/{name}/command/{commandname}: - get: - description: Issue the GET command referenced by the command name to the device/sensor, - also referenced by name. It is associated to via the device service. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: commandname - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: String as returned by the device/sensor via the device service. - 400: - description: If the request is malformed or unparsable - 404: - description: If device with given name does not exist or device doesn't - have command with the given command name. - 423: - description: If the device is locked in an admin state. - 500: - description: For unanticipated or unknown issues encountered. - put: - description: Issue the PUT command referenced by the command name to the device/sensor - also referenced by name it is associated to via the device service. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: commandname - in: path - required: true - style: simple - explode: false - schema: - type: string - requestBody: - $ref: '#/components/requestBodies/setting' - responses: - 200: - description: String as returned by the device/sensor via the device service. - 400: - description: If the request is malformed or unparsable - 404: - description: If device with given name does not exist or device doesn't - have a command with the given command name. - 423: - description: If the device is locked in an admin state - 500: - description: For unanticipated or unknown issues encountered - /v1/device/{id}: - get: - description: Retrieve a device by database generated ID and its available commands. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: CommandResponse containing the device and its commands. - content: - application/json: - schema: - $ref: '#/components/schemas/commandresponse' - 404: - description: If no device exists by the ID provided. - 500: - description: For unanticipated or unknown issues encountered. - /v1/device/{id}/command/{commandid}: - get: - description: Issue the GET command referenced by the command ID to the device/sensor, - also referenced by database generated ID that it is associated to via the device - service. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: commandid - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: String as returned by the device/sensor via the device service. - 400: - description: If the request is malformed or unparsable - 404: - description: If no device exists by the ID provided - 423: - description: If the device is locked in an admin state. - 500: - description: For unanticipated or unknown issues encountered. - put: - description: Issue the PUT command referenced by the command ID to the device/sensor, - also referenced by database generated ID that it is associated to via the device - service. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: commandid - in: path - required: true - style: simple - explode: false - schema: - type: string - requestBody: - $ref: '#/components/requestBodies/setting' - responses: - 200: - description: String as returned by the device/sensor via the device service. - content: - '*/*': - schema: - $ref: '#/components/schemas/setting' - 400: - description: If the request is malformed or unparsable - 404: - description: If no device exists by the ID provided - 423: - description: If the device is locked in an admin state - 500: - description: For unanticipated or unknown issues encountered. - /v1/metrics: - get: - description: Fetch the current state of the service's metrics. - responses: - 200: - description: The service's metrics as JSON document - /v1/ping: - get: - description: Test service providing an indication that the service is available. - responses: - 200: - description: String literal confirming that the service is up. - 500: - description: For unanticipated or unknown issues encountered. - /version: - get: - description: Get the API version - responses: - 200: - description: The service's API version as JSON document -components: - schemas: - addressable: - title: addressable - type: object - properties: - address: - title: address - type: string - created: - title: created - type: integer - id: - title: id - type: string - modified: - title: modified - type: integer - name: - title: name - type: string - origin: - title: origin - type: integer - password: - title: password - type: string - path: - title: path - type: string - port: - title: port - type: integer - protocol: - title: protocol - type: string - publisher: - title: publisher - type: string - topic: - title: topic - type: string - user: - title: user - type: string - commandresponse: - title: commandresponse - type: object - properties: - host: - title: host - type: string - device: - $ref: '#/components/schemas/device' - device: - title: device - type: object - properties: - addressable: - $ref: '#/components/schemas/device_addressable' - adminState: - title: adminState - type: string - created: - title: created - type: integer - description: - title: description - type: string - id: - title: id - type: string - labels: - title: labels - uniqueItems: false - type: array - items: - title: labels - type: string - lastConnected: - title: lastConnected - type: integer - lastReported: - title: lastReported - type: integer - modified: - title: modified - type: integer - name: - title: name - type: string - operatingState: - title: operatingState - type: string - origin: - title: origin - type: integer - description: device or sensor supplying data and taking actuation commands - setting: - title: setting - type: object - additionalProperties: - type: string - device_addressable: - type: object - properties: - address: - title: address - type: string - created: - title: created - type: integer - id: - title: id - type: string - modified: - title: modified - type: integer - name: - title: name - type: string - origin: - title: origin - type: integer - password: - title: password - type: string - path: - title: path - type: string - port: - title: port - type: integer - protocol: - title: protocol - type: string - publisher: - title: publisher - type: string - topic: - title: topic - type: string - user: - title: user - type: string - requestBodies: - setting: - content: - application/json: - schema: - $ref: '#/components/schemas/setting' - required: true diff --git a/openapi/v1/core-data.yaml b/openapi/v1/core-data.yaml deleted file mode 100644 index c9962feffb..0000000000 --- a/openapi/v1/core-data.yaml +++ /dev/null @@ -1,1061 +0,0 @@ -openapi: 3.0.0 -info: - title: core-data - version: 1.2.1 -servers: -- url: http://localhost:48080/api -paths: - /v1/config: - get: - description: Fetch the current state of the service's configuration. - responses: - 200: - description: The service's configuration as JSON document - 400: - description: Request is invalid or unparseable or if the - underlying configuration cannot be serialized to JSON properly. - /v1/event: - get: - description: Fetch all events with their associated readings. - responses: - 200: - description: List of events - content: - '*/*': - schema: - $ref: '#/components/schemas/event' - 413: - description: If the number of events exceeds the current max limit. - 500: - description: For unknown or unanticipated issues. - put: - description: Update the event data, not including updating the readings. - requestBody: - $ref: '#/components/requestBodies/event' - responses: - 200: - description: Boolean on success of update request - 400: - description: Update request is invalid - 404: - description: If the event cannot be found by ID. - 500: - description: For unknown or unanticipated issues. - post: - description: Add a new event, along with its associated readings. - requestBody: - $ref: '#/components/requestBodies/event' - responses: - 200: - description: New event ID - 400: - description: Creation request is invalid - 404: - description: If a reading is associated with a non-existing value descriptor, - or if device verification is enabled and the device is not found. - 500: - description: For unknown or unanticipated issues. - /v1/event/checksum/{checksum}: - put: - description: Update an existing event's pushed time - parameters: - - name: checksum - in: path - description: Checksum value of the event provided by core-data via the message - bus - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Success - 404: - description: If the event cannot be found by checksum. - 500: - description: For unknown or unanticipated issues. - /v1/event/count: - get: - description: Return a count of the number of events in core data. - responses: - 200: - description: Number of events in the collection - 500: - description: For unknown or unanticipated issues. - /v1/event/count/{deviceId}: - get: - description: Return a count of the number of events in core data for a given - device, identified by ID or name. - parameters: - - name: deviceId - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Number of events for the device - 400: - description: Query request is invalid - 500: - description: For unknown or unanticipated issues. - /v1/event/device/{deviceId}: - delete: - description: Delete all events and the readings associated with a device, given - the device's ID. - parameters: - - name: deviceId - in: path - description: The ID or name of the device associated - to events - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Count of the number of events deleted - 400: - description: Could not unescape URL - 404: - description: If the meta data checks are on and no device is found - for the supplied ID. - 500: - description: For unknown or unanticipated issues. - /v1/event/device/{deviceId}/{limit}: - get: - description: Return a list of events with associated readings for a given device, - sorted by event modified date. - parameters: - - name: limit - in: path - description: Maximum number of events to fetch, must be less than the max limit - required: true - style: simple - explode: false - schema: - type: integer - - name: deviceId - in: path - description: The ID or name of the associated device. - to events - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of events associated with the matching device by ID. - content: - '*/*': - schema: - $ref: '#/components/schemas/event' - 400: - description: Request is invalid or unparseable - 404: - description: If the meta data checks are on and no device is found for the - supplied ID. - 413: - description: If the number of events exceeds the current max limit. - 500: - description: For unknown or unanticipated issues. - /v1/event/id/{id}: - put: - description: Update the event to be pushed out of EdgeX to an enterprise or - cloud system. - parameters: - - name: id - in: path - description: Database-generated ID - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of update request - 404: - description: If the event cannot be found by ID - 500: - description: For unknown or unanticipated issues. - delete: - description: Delete an event and all its readings. - parameters: - - name: id - in: path - description: Database-generated ID - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of deletion request - 404: - description: If the event cannot be found by ID. - 500: - description: For unknown or unanticipated issues. - /v1/event/removeold/age/{age}: - delete: - description: Remove all old events and associated readings - based on delimiting age. - parameters: - - name: age - in: path - description: Minimum age in milliseconds (from created timestamp) an event - should be in order to be removed - required: true - style: simple - explode: false - schema: - type: integer - responses: - 200: - description: Count of the number of events removed - 500: - description: For unknown or unanticipated issues. - /v1/event/scrub: - delete: - description: Remove all pushed events and their associated readings, and - should only be used by the scrubber micro service. - responses: - 200: - description: Count of the number of events scrubbed - 500: - description: For unknown or unanticipated issues. - /v1/event/{id}: - get: - description: Fetch a specific event by database specified ID, returning null - if none are found. - parameters: - - name: id - in: path - description: Database-generated ID - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Event - content: - '*/*': - schema: - $ref: '#/components/schemas/event' - 404: - description: If the event cannot be found by ID. - 500: - description: For unknown or unanticipated issues. - /v1/event/{start}/{end}/{limit}: - get: - description: Return all events between a given begin and end date/time sorted - by event modified date, with newest events at the top. - parameters: - - name: limit - in: path - description: Maximum number of events to fetch, must be less than max limit - required: true - style: simple - explode: false - schema: - type: integer - - name: start - in: path - description: Start date in long form - required: true - style: simple - explode: false - schema: - type: integer - - name: end - in: path - description: End date in long form - required: true - style: simple - explode: false - schema: - type: integer - responses: - 200: - description: List of events between the specified dates - content: - '*/*': - schema: - $ref: '#/components/schemas/event' - 400: - description: Request is invalid or unparseable - 413: - description: If the number of events exceeds the current max limit. - 500: - description: For unknown or unanticipated issues. - /v1/ping: - get: - description: Test service providing an indication that the service is available. - responses: - 200: - description: Return value of "pong" - 503: - description: For unknown or unanticipated issues - /v1/reading: - get: - description: Return list of all readings, sorted by reading ID. - responses: - 200: - description: List of all readings - content: - '*/*': - schema: - $ref: '#/components/schemas/reading' - 413: - description: If the number of readings exceeds the current max limit. - 500: - description: For unknown or unanticipated issues - put: - description: Update the reading - requestBody: - $ref: '#/components/requestBodies/reading' - responses: - 200: - description: Boolean indicating success of the update operation - 400: - description: Request is invalid or unparseable - 404: - description: If the reading cannot be found by ID - 409: - description: If the associated value descriptor is non-existent. - 500: - description: For unknown or unanticipated issues - post: - description: Add a new reading. - requestBody: - $ref: '#/components/requestBodies/reading' - responses: - 200: - description: String ID (database ID) of the new Reading - 400: - description: Request is invalid or unparseable - 409: - description: If the associated value descriptor is non-existent - 500: - description: For unknown or unanticipated issues. - /v1/reading/count: - get: - description: Return a count of the number of readings in core data. - responses: - 200: - description: Number of readings in the collection - 500: - description: For unknown or unanticipated issues. - /v1/reading/device/{deviceId}/{limit}: - get: - description: Return a list of all readings for a given device, sorted - by the readings modified date. - parameters: - - name: limit - in: path - description: Maximum number of readings requested (not to exceed max limit) - required: true - style: simple - explode: false - schema: - type: integer - - name: deviceId - in: path - description: Device ID or device name - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of readings for a device, could be an empty list if none - match - content: - '*/*': - schema: - $ref: '#/components/schemas/reading' - 400: - description: Request is invalid or unparseable - 404: - description: If meta checks are in place and if the device ID or name does - not match any existing devices - 413: - description: If the number of readings exceeds the current max limit. - 500: - description: Unknown or unanticipated issues - /v1/reading/id/{id}: - delete: - description: Delete the reading from persistent storage. - parameters: - - name: id - in: path - description: The ID for which the reading is to be deleted - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean indicating success of the delete operation - 404: - description: If the reading cannot be found by ID - 500: - description: For unknown or unanticipated issues. - /v1/reading/label/{label}/{limit}: - get: - description: Return a list of readings with an associated value descriptor of - the label specified, sorted by the readings modified date. - parameters: - - name: limit - in: path - description: Maximum number of readings to return (must not exceed max limit) - required: true - style: simple - explode: false - schema: - type: integer - - name: label - in: path - description: Label that should be in matching Value Descriptor's label array - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of matching readings having value descriptor with the - associated label. Could be an empty list if none match. - content: - '*/*': - schema: - $ref: '#/components/schemas/reading' - 400: - description: Request is invalid or unparseable - 413: - description: If the number of readings exceeds the current max limit. - 500: - description: For unknown or unanticipated issues. - /v1/reading/name/{name}/device/{device}/{limit}: - get: - description: Return a list of readings that are associated with a value descriptor - by name and Device by name or ID, sorted by the readings modified date. - parameters: - - name: limit - in: path - description: Maximum number of readings to return (must not exceed max limit) - required: true - style: simple - explode: false - schema: - type: integer - - name: name - in: path - description: Name of the matching value descriptor - required: true - style: simple - explode: false - schema: - type: string - - name: device - in: path - description: Name or ID of the matching device - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of readings matching on the value descriptor name and - device name (or ID) - content: - '*/*': - schema: - $ref: '#/components/schemas/reading' - 400: - description: Request is invalid or unparseable - 413: - description: If the number of readings exceeds the current max limit. - 500: - description: For unknown or unanticipated issues. - /v1/reading/name/{name}/{limit}: - get: - description: Return a list of readings that are associated with a value - descripter by name, sorted by the readings modified date. - parameters: - - name: limit - in: path - description: Maximum number of readings to return (must not exceed max limit) - required: true - style: simple - explode: false - schema: - type: integer - - name: name - in: path - description: Name of the matching value descriptor - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of readings matching on the value descriptor name - content: - '*/*': - schema: - $ref: '#/components/schemas/reading' - 400: - description: Request is invalid or unparseable - 413: - description: If the number of readings exceeds the current max limit. - 500: - description: For unknown or unanticipated issues. - /v1/reading/type/{type}/{limit}: - get: - description: Return a list of readings with an associated value descriptor of - the type IoTType specified, sorted by the readings modified date. - parameters: - - name: limit - in: path - description: maximum number of readings to be allowed to be returned - required: true - style: simple - explode: false - schema: - type: integer - - name: type - in: path - description: An IoTType in string form, being one of I, B, F, S for - integer, Boolean, Floating point or String. - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of matching readings having value descriptor of the types - specified. - content: - '*/*': - schema: - $ref: '#/components/schemas/reading' - 400: - description: request is invalid or unparseable - 413: - description: if the number of readings exceeds the current max limit. - 500: - description: for unknown or unanticipated issues. - /v1/reading/uomlabel/{uomLabel}/{limit}: - get: - description: Return a list of readings with an associated value descriptor of - the UoM label specified, sorted by the readings modified date. - parameters: - - name: limit - in: path - description: exceed max limit) - required: true - style: simple - explode: false - schema: - type: integer - - name: uomLabel - in: path - description: Unit of Measure label (UoMLabel) matching the value descriptor - associated with the reading - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of matching readings having value descriptor with UoM - label specified - content: - '*/*': - schema: - $ref: '#/components/schemas/reading' - 400: - description: Request is invalid or unparseable - 413: - description: If the number of readings exceeds the current max limit. - 500: - description: For unknown or unanticipated issues. - /v1/reading/{id}: - get: - description: Retrieve a reading by its ID. - parameters: - - name: id - in: path - description: Reading database-generated ID - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Reading - content: - '*/*': - schema: - $ref: '#/components/schemas/reading' - 404: - description: if the reading cannot be found by ID - 500: - description: for unknown or unanticipated issues - /v1/reading/{start}/{end}/{limit}: - get: - description: Return a list of readings between two timestamps which - is limited by the number specified in the limit parameter, being sorted - by the readings modified date. - parameters: - - name: limit - in: path - description: Maximum number of readings to be allowed to be returned - required: true - style: simple - explode: false - schema: - type: integer - - name: start - in: path - description: Millisecond timestamp of the beginning of the time range - required: true - style: simple - explode: false - schema: - type: integer - - name: end - in: path - description: Millisecond timestamp of the end of the time rage - required: true - style: simple - explode: false - schema: - type: integer - responses: - 200: - description: list of matching readings in this range (limited by the limit - parameter) - content: - '*/*': - schema: - $ref: '#/components/schemas/reading' - 400: - description: Request is invalid or unparseable - 413: - description: If the number of readings exceeds the current max limit. - 500: - description: For unknown or unanticipated issues. - /v1/valuedescriptor: - get: - description: Return all value descriptor objects. - responses: - 200: - description: List of value descriptors - content: - '*/*': - schema: - $ref: '#/components/schemas/valueDescriptor' - 413: - description: If the number of value descriptors exceeds the current max - limit - 500: - description: For unknown or unanticipated issues. - put: - description: Update the value descriptor identified by the ID or name in the - object provided. - requestBody: - $ref: '#/components/requestBodies/valueDescriptor' - responses: - 200: - description: Boolean indicating success of the update - 400: - description: Request is invalid or unparseable - 404: - description: If the value descriptor cannot be located by the identifier. - 500: - description: For unknown or unanticipated issues - post: - description: Add a new value descriptor whose name must be unique. - requestBody: - $ref: '#/components/requestBodies/valueDescriptor' - responses: - 200: - description: The ID of the new value descriptor - 400: - description: Request is invalid or unparseable - 409: - description: If the a formatting string of the value descriptor is not a - valid printf format or if the name is determined to not be unique with - regard to other value descriptors. - 500: - description: for unknown or unanticipated issues - /v1/valuedescriptor/deviceid/{id}: - get: - description: Fetch all value descriptors that are associated with a - Device's command set as either a put command parameter name or - get/put response expected value. - parameters: - - name: id - in: path - description: The ID of the device - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: list of value descriptors - content: - '*/*': - schema: - $ref: '#/components/schemas/valueDescriptor' - 400: - description: If the request is malformed or unparsable - 404: - description: If the device cannot be found by the ID provided - 500: - description: For unknown or unanticipated issues. - /v1/valuedescriptor/devicename/{name}: - get: - description: Fetch all value descriptors that are associated with - a Device's command set. - parameters: - - name: name - in: path - description: Unique name of the device - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of value descriptors - content: - '*/*': - schema: - $ref: '#/components/schemas/valueDescriptor' - 400: - description: If the request is malformed or unparsable - 404: - description: If the device cannot be found by the name provided - 500: - description: For unknown or unanticipated issues. - /v1/valuedescriptor/id/{id}: - delete: - description: Remove the value descriptor designated by by its ID. - parameters: - - name: id - in: path - description: Value descriptor's ID - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean indicating success of the remove operation - 400: - description: ID is invalid or unparseable - 404: - description: If the value descriptor cannot be located by the identifier. - 409: - description: If the value descriptor is still referenced in Readings - 500: - description: For unknown or unanticipated issues - /v1/valuedescriptor/label/{label}: - get: - description: Return value descriptor objects with given label. - parameters: - - name: label - in: path - description: Tag or label - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of value descriptor matching UoM Label - content: - '*/*': - schema: - $ref: '#/components/schemas/valueDescriptor' - 400: - description: Request is invalid or unparseable - 500: - description: For unknown or unanticipated issues. - /v1/valuedescriptor/name/{name}: - get: - description: Return value descriptor object with given name, which can be null if - no value descriptors found by the name. - parameters: - - name: name - in: path - description: Unique name of the value descriptor - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: value descriptor having the provided name, could be null if - none are found. - content: - '*/*': - schema: - $ref: '#/components/schemas/valueDescriptor' - 400: - description: Request is invalid or unparseable - 404: - description: If the value descriptor cannot be located by the name - 500: - description: For unknown or unanticipated issues. - delete: - description: Remove the value descriptor designated by name. - parameters: - - name: name - in: path - description: Unique name of the value descriptor - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean indicating success of the remove operation - 400: - description: Request is invalid or unparseable - 404: - description: If the value descriptor cannot be located by the identifier - 409: - description: If the value descriptor is still referenced in Readings - 500: - description: For unknown or unanticipated issues. - /v1/valuedescriptor/uomlabel/{uomLabel}: - get: - description: Return value descriptor objects with given UoM label. - parameters: - - name: uomLabel - in: path - description: Unit of measure - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of value descriptors matching UoM Label - content: - '*/*': - schema: - $ref: '#/components/schemas/valueDescriptor' - 400: - description: Request is invalid or unparseable - 500: - description: For unknown or unanticipated issues. - /v1/valuedescriptor/usage: - get: - description: Get a list of value descriptors and usage state. - parameters: - - name: names - in: query - description: A comma separated list of value descriptor names for which to - perform the usage check. - required: true - style: form - explode: true - schema: - type: string - responses: - 200: - description: List of value descriptors and thier current usage state - 400: - description: Malformed query parameters - 413: - description: The maximum number of results has been exceeded - 500: - description: For unknown or unanticipated issues - /v1/valuedescriptor/{id}: - get: - description: Fetch a specific value descriptor by its ID. - parameters: - - name: id - in: path - description: The ID for the value descriptor - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Value descriptor - 404: - description: If the value descriptor cannot be located by the identifier - 500: - description: For unknown or unanticipated issues - /version: - get: - description: Get the API version - responses: - 200: - description: The service's API version as JSON document -components: - schemas: - event: - title: event - type: object - properties: - created: - title: created - type: integer - device: - title: device - type: string - id: - title: id - type: string - modified: - title: modified - type: integer - origin: - title: origin - type: integer - pushed: - title: pushed - type: integer - readings: - title: readings - uniqueItems: false - type: array - items: - $ref: '#/components/schemas/reading' - tags: - title: tags - description: "List of zero or more Tags attached to the Event which give more context to the Event" - uniqueItems: false - type: object - example: { - "Gateway-id": "HoustonStore-000123", - "Latitude": "29.630771", - "Longitude": "-95.377603", - } - description: Core device/sensor event - reading: - title: reading - type: object - properties: - created: - title: created - type: integer - id: - title: id - type: string - modified: - title: modified - type: integer - name: - title: name - type: string - origin: - title: origin - type: integer - pushed: - title: pushed - type: integer - value: - title: value - type: string - description: Core device/sensor reading - valueDescriptor: - title: valueDescriptor - type: object - properties: - created: - title: created - type: integer - defaultValue: - title: defaultValue - type: string - description: - title: description - type: string - formatting: - title: formatting - type: string - id: - title: id - type: string - labels: - title: labels - uniqueItems: false - type: array - items: - title: labels - type: string - max: - title: max - type: string - min: - title: min - type: string - modified: - title: modified - type: integer - name: - title: name - type: string - origin: - title: origin - type: integer - type: - title: type - type: string - uomLabel: - title: uomLabel - type: string - description: Core and MetaData value descriptor that describes - device/sensor data sent and received - requestBodies: - event: - content: - application/json: - schema: - $ref: '#/components/schemas/event' - required: true - reading: - content: - application/json: - schema: - $ref: '#/components/schemas/reading' - required: true - valueDescriptor: - content: - application/json: - schema: - $ref: '#/components/schemas/valueDescriptor' - required: true diff --git a/openapi/v1/core-metadata.yaml b/openapi/v1/core-metadata.yaml deleted file mode 100644 index e351990919..0000000000 --- a/openapi/v1/core-metadata.yaml +++ /dev/null @@ -1,2460 +0,0 @@ -openapi: 3.0.0 -info: - title: core-metadata - version: 1.2.1 -servers: -- url: http://localhost:48081/api -paths: - /v1/addressable: - get: - description: Return all addressable objects sorted by database generated ID. - responses: - 200: - description: List of addressable - content: - '*/*': - schema: - $ref: '#/components/schemas/addressable' - 413: - description: If the number returned exceeds the max limit. - 500: - description: For unknown or unanticipated issues. - put: - description: Update the addressable identified by the ID or name in the object - provided. - requestBody: - $ref: '#/components/requestBodies/addressable' - responses: - 200: - description: Boolean on success of update request - 400: - description: For malformed or unparsable requests - 404: - description: If no addressable with the provided ID is found. - 409: - description: If the addressable is currently in use - 500: - description: For unknown or unanticipated issues or for any duplicate name - (key) error. - post: - description: Add a new addressable, where name must be unique. - requestBody: - $ref: '#/components/requestBodies/addressable' - responses: - 200: - description: New database generated ID for the new addressable - 400: - description: For malformed or unparsable requests - 409: - description: If the name is determined to not be unique with regard to others - 500: - description: For unknown or unanticipated issues or for any duplicate name - (key) error. - /v1/addressable/address/{address}: - get: - description: Return addressable objects with given address. List may be empty - if none are associated with the address. - parameters: - - name: address - in: path - description: Address (as in URL address) - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of addressable matching address - content: - '*/*': - schema: - $ref: '#/components/schemas/addressable' - 400: - description: For malformed or unparsable requests - 500: - description: For unknown or unanticipated issues. - /v1/addressable/id/{id}: - delete: - description: Remove the addressable designated by the database generated ID - for the addressable. - parameters: - - name: id - in: path - description: Database generated ID - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of delete request - 404: - description: If no addressable with the provided ID is found. - 409: - description: If the adressable is still in use - 500: - description: For unknown or unanticipated issues - /v1/addressable/name/{name}: - get: - description: Return addressable with matching name, where name should be unique. - parameters: - - name: name - in: path - description: Unique name of addressable - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: addressable matching on name - content: - '*/*': - schema: - $ref: '#/components/schemas/addressable' - 400: - description: For malformed or unparsable requests - 404: - description: If no addressable with the provided name is found - 500: - description: For unknown or unanticipated issues. - delete: - description: Remove the addressable designated by unique name ID.. - parameters: - - name: name - in: path - description: Unique name of addressable - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of delete request - 400: - description: For malformed or unparsable requests - 404: - description: If no addressable with the provided name is found. - 500: - description: For unknown or unanticipated issues. - /v1/addressable/port/{port}: - get: - description: Return addressable objects with given port. List may be empty if - none are associated with the port. - parameters: - - name: port - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of addressable matching port - content: - '*/*': - schema: - $ref: '#/components/schemas/addressable' - 400: - description: For malformed or unparsable requests - 500: - description: For unknown or unanticipated issues - /v1/addressable/publisher/{publisher}: - get: - description: Return addressable objects with given publisher. List may be empty - if none are associated with the publisher. - parameters: - - name: publisher - in: path - description: Publisher name - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of addressable matching publisher - content: - '*/*': - schema: - $ref: '#/components/schemas/addressable' - 400: - description: For malformed or unparsable requests - 500: - description: For unknown or unanticipated issues. - /v1/addressable/topic/{topic}: - get: - description: Return addressable objects with given topic. List may be empty - if none are associated with the topic. - parameters: - - name: topic - in: path - description: Topic name, as used by the messaging provider. - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of addressable matching topic - content: - '*/*': - schema: - $ref: '#/components/schemas/addressable' - 400: - description: For malformed or unparsable requests - 500: - description: for unknown or unanticipated issues. - /v1/addressable/{id}: - get: - description: Fetch a specific addressable by database generated ID. May return - null if no addressable matches on ID. - parameters: - - name: id - in: path - description: Database generated ID - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: addressable - content: - '*/*': - schema: - $ref: '#/components/schemas/addressable' - 404: - description: If no addressable with the provided ID is found - 500: - description: For unknown or unanticipated issues - /v1/command: - get: - description: Return all command objects. - responses: - 200: - description: Boolean on success of get request - content: - '*/*': - schema: - $ref: '#/components/schemas/command' - 413: - description: if the number returned exceeds the max limit. - 500: - description: for unknown or unanticipated errors - /v1/command/device/{id}: - get: - description: Fetch commands by device ID. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of get request - content: - '*/*': - schema: - $ref: '#/components/schemas/command' - 404: - description: If no device exists for the provided ID. - 500: - description: For unknown or unanticipated issues. - /v1/command/name/{name}: - get: - description: Return command objects with given name. Name is not unique for - all of EdgeX but is unique per any associated device profile. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of get request - content: - '*/*': - schema: - $ref: '#/components/schemas/command' - 400: - description: For malformed or unparsable requests - 500: - description: For unknown or unanticipated issues. - /v1/command/{id}: - get: - description: Fetch a specific command by database generated ID. May return null - if no commands with the ID is found. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of get request - content: - '*/*': - schema: - $ref: '#/components/schemas/command' - 404: - description: If no command with the provided ID is found. - 500: - description: For unknown or unanticipated issues. - /v1/config: - get: - description: Fetch the current state of the service's configuration. - responses: - 200: - description: The service's configuration as JSON document - 400: - description: Request is invalid or unparseable or if the - underlying configuration cannot be serialized to JSON properly. - /v1/device: - get: - description: Return all devices sorted by ID. - responses: - 200: - description: List of device - content: - '*/*': - schema: - $ref: '#/components/schemas/device' - 413: - description: If the number returned exceeds the max limit - 500: - description: For unknown or unanticipated issues - put: - description: Update the device identified by the ID or name stored in the object - provided. - requestBody: - $ref: '#/components/requestBodies/device' - responses: - 200: - description: Boolean on success of update request - 400: - description: If the request is malformed or unparsable - 404: - description: If the device cannot be found by the ID provided. - 500: - description: For unknown or unanticipated issues. - post: - description: Add a new device, where name must be unique. - requestBody: - $ref: '#/components/requestBodies/device' - responses: - 200: - description: Database generated ID for the new device - 400: - description: If the request is malformed or unparsable or if an associated - object, such as addressable, profile, service cannot be found with the ID or - name provided - 409: - description: If the name is determined to not be unique with regard to others. - 500: - description: For unknown or unanticipated issues. - /v1/device/check/{token}: - get: - description: Obtain a device by either name or ID, where the ID must be a - valid GUID. - parameters: - - name: token - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: JSON payload representing the device - 400: - description: For incorrect or unparsable requests - 404: - description: Device was not found - /v1/device/id/{id}: - delete: - description: Remove the device designated by database generated ID. This does - not remove associated objects such as addressable, service, profile, etc. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of delete request - 400: - description: For incorrect or unparsable requests - 404: - description: If the device cannot be found by the ID provided. - /v1/device/label/{label}: - get: - description: Find all devices having at least one label matching the label provided. - List may be empty if no device match. - parameters: - - name: label - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of devices matching on specified label - content: - '*/*': - schema: - $ref: '#/components/schemas/device' - 400: - description: For incorrect or unparsable requests - 500: - description: For unknown or unanticipated issues. - /v1/device/name/{name}: - get: - description: Return device matching given name, where device name should be unique. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Device matching on name - content: - '*/*': - schema: - $ref: '#/components/schemas/device' - 400: - description: For incorrect or unparsable requests - 404: - description: If the device cannot be found by the name provided. - 500: - description: For unknown or unanticipated issues. - put: - description: Set the admin or operating state of the device by unique name of - the device. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - requestBody: - $ref: '#/components/requestBodies/device' - responses: - 200: - description: Boolean on success of update request - 400: - description: For incorrect or unparsable requests - 404: - description: If the device cannot be found by the name provided. - 500: - description: For unknown or unanticipated issues. - delete: - description: Remove the device designated by unique name. This does not remove - associated objects such as addressable, service, profile, etc. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of delete request - 400: - description: For incorrect or unparsable requests - 404: - description: If the device cannot be found by the name provided. - /v1/device/name/{name}/lastconnected/{time}: - put: - description: Update the last connected time of the device by unique name of - the device. - parameters: - - name: time - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of update request - 400: - description: For incorrect or unparsable requests - 404: - description: If the device cannot be found by the name provided. - 500: - description: For unknown or unanticipated issues. - /v1/device/name/{name}/lastconnected/{time}/{notify}: - put: - description: Update the last connected time of the device by unique name of - the device. - parameters: - - name: time - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: notify - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of update request - 400: - description: For incorrect or unparsable requests - 404: - description: If the device cannot be found by the name provided. - 500: - description: For unknown or unanticipated issues. - /v1/device/name/{name}/lastreported/{time}: - put: - description: Update the last reported time of the device by unique name of the - device. - parameters: - - name: time - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of update request - 400: - description: For incorrect or unparsable requests - 404: - description: If the device cannot be found by the name provided. - 500: - description: For unknown or unanticipated issues. - /v1/device/name/{name}/lastreported/{time}/{notify}: - put: - description: Update the last reported time of the device by unique name of the - device. - parameters: - - name: time - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: notify - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of update request - 400: - description: For incorrect or unparsable requests - 404: - description: If the device cannot be found by the name provided. - 500: - description: For unknown or unanticipated issues. - /v1/device/profile/{profileId}: - get: - description: Find all devices associated with the device profile with the specified - profile database generated ID. List may be empty if no device match. - parameters: - - name: profileId - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of devices associated with the device profile - content: - '*/*': - schema: - $ref: '#/components/schemas/device' - 404: - description: If no device profile match on the ID provided. - 500: - description: For unknown or unanticipated issues. - /v1/device/profilename/{profilename}: - get: - description: Find all devices associated with the device profile with the specified - profile name. List may be empty if no device match. - parameters: - - name: profilename - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of devices associated with the profile - content: - '*/*': - schema: - $ref: '#/components/schemas/device' - 400: - description: For incorrect or unparsable requests - 404: - description: If the device cannot be found by the ID provided. - 500: - description: For unknown or unanticipated issues. - /v1/device/service/{serviceId}: - get: - description: Find all devices associated with the device service with the specified - device service database generated ID. List may be empty if no device - match. - parameters: - - name: serviceId - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of devices associated with the device service - content: - '*/*': - schema: - $ref: '#/components/schemas/device' - 404: - description: If no device service match on the ID provided. - 500: - description: For unknown or unanticipated issues. - /v1/device/servicename/{servicename}: - get: - description: Find all devices associated with the device service with the specified - service name where device service names must be unique. - parameters: - - name: servicename - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of devices associated with the device service - content: - '*/*': - schema: - $ref: '#/components/schemas/device' - 400: - description: For incorrect or unparsable requests - 404: - description: If the device cannot be found by the name provided. - 500: - description: For unknown or unanticipated issues. - /v1/device/{id}: - get: - description: Fetch a specific device by database generated ID. May return null - if no device with the ID is found. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Device matching on the ID - content: - '*/*': - schema: - $ref: '#/components/schemas/device' - 400: - description: For incorrect or unparsable requests - 404: - description: If the device cannot be found by the name provided. - 500: - description: For unknown or unanticipated issues. - put: - description: Set the admin or operating state of the device, as referenced by - the database generated ID of the device to the state provided, either enabled - or disabled. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - requestBody: - $ref: '#/components/requestBodies/device' - responses: - 200: - description: Status code indicating success - 400: - description: For incorrect or unparsable requests - 404: - description: If no device exists by the ID provided. - 500: - description: For unanticipated or unknown issues encountered. - /v1/device/{id}/lastconnected/{time}: - put: - description: Update the last connected time of the device by database generated ID. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: time - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of update request - 400: - description: For incorrect or unparsable requests - 404: - description: If the device cannot be found by the name provided. - 500: - description: For unknown or unanticipated issues. - /v1/device/{id}/lastconnected/{time}/{notify}: - put: - description: Update the last connected time of the device by database generated ID. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: time - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: notify - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of update request - 400: - description: For incorrect or unparsable requests - 404: - description: If the device cannot be found by the name provided. - 500: - description: For unknown or unanticipated issues. - /v1/device/{id}/lastreported/{time}: - put: - description: Update the last reported time of the device by database generated ID. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: time - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of update request - 400: - description: For incorrect or unparsable requests - 404: - description: If the device cannot be found by the name provided. - 500: - description: For unknown or unanticipated issues. - /v1/device/{id}/lastreported/{time}/{notify}: - put: - description: Update the last reported time of the device by database generated ID. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: time - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: notify - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of update request - 400: - description: For incorrect or unparsable requests - 404: - description: If the device cannot be found by the name provided. - 500: - description: For unknown or unanticipated issues. - /v1/deviceprofile: - get: - description: Return all profiles sorted by ID. - responses: - 200: - description: List of profiles - content: - '*/*': - schema: - $ref: '#/components/schemas/deviceprofile' - 413: - description: If the number returned exceeds the max limit - 500: - description: For unknown or unanticipated issues - put: - description: Update the device profile identified by the ID or name stored in - the object provided. - requestBody: - $ref: '#/components/requestBodies/deviceprofile' - responses: - 200: - description: Boolean on success of delete request - 400: - description: For malformed or unparsable requests - 404: - description: If the profile cannot be found by the ID provided - 409: - description: If the profile contains duplicate command or profile names, - or if the device profile is in use by one or more provision watchers or - devices. - 500: - description: For unknown or unanticipated issues - post: - description: Add a new device profile, and associated command objects, where name - must be unique. - requestBody: - $ref: '#/components/requestBodies/deviceprofile' - responses: - 200: - description: Database generated ID for the new device profile - 400: - description: For malformed or unparsable requests - 409: - description: If the profile's name matches an existing device profile. - 500: - description: For unknown or unanticipated issues - /v1/deviceprofile/id/{id}: - delete: - description: Remove the device profile designated by database generated ID. This - does not remove associated commands. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of delete request - 404: - description: If the device profile cannot be found with the ID provided - 409: - description: If devices or provision watchers still reference the profile - 500: - description: For unknown or unanticipated issues - /v1/deviceprofile/label/{label}: - get: - description: Find all device profiles having at least one label matching the - label provided. List may be empty if no profiles match. - parameters: - - name: label - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of device profile matching on specified label - content: - '*/*': - schema: - $ref: '#/components/schemas/deviceprofile' - 400: - description: For malformed or unparsable requests - 500: - description: for unknown or unanticipated issues - /v1/deviceprofile/manufacturer/{manufacturer}: - get: - description: Find all device profiles with a manufacture attribute matching that - provided. List may be empty if no profiles match. - parameters: - - name: manufacturer - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of device profile matching on specified manufacturer. - content: - '*/*': - schema: - $ref: '#/components/schemas/deviceprofile' - 400: - description: For malformed or unparsable requests - 500: - description: For unknown or unanticipated issues - /v1/deviceprofile/manufacturer/{manufacturer}/model/{model}: - get: - description: Find all device profiles with a manufacturer or model attribute matching - that provided, either matching provides a hit). List may be empty if no profiles - match. - parameters: - - name: model - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: manufacturer - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of device profile matching on specified manufacturer and/or - model. - content: - '*/*': - schema: - $ref: '#/components/schemas/devicereport' - 400: - description: For malformed or unparsable requests - 500: - description: For unknown or unanticipated issues - /v1/deviceprofile/model/{model}: - get: - description: Find all device profiles with a model attribute matching that provided. - List may be empty if no profiles match. - parameters: - - name: model - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of device profile matching on specified model. - content: - '*/*': - schema: - $ref: '#/components/schemas/deviceprofile' - 400: - description: For malformed or unparsable requests - 500: - description: For unknown or unanticipated issues - /v1/deviceprofile/name/{name}: - get: - description: Return the device profile matching given name, where profile names should - be unique. May be null if no profiles matches on the name provided. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Device profile matching on name - content: - '*/*': - schema: - $ref: '#/components/schemas/deviceprofile' - 400: - description: For malformed or unparsable requests - 404: - description: If the device profile cannot be found by the name provided - 500: - description: For unknown or unanticipated issues - delete: - description: Remove the device profile designated by unique name. This does not - remove associated commands. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of delete request - 400: - description: For malformed or unparsable requests - 404: - description: If the device profile cannot be found by the name provided - 409: - description: If devices or provision watchers still reference the profile - 500: - description: For unknown or unanticipated issues - /v1/deviceprofile/upload: - post: - description: Add a new device profile (and associated command objects) via YAML - content, where name must be unique. - responses: - 200: - description: Database generated ID for the new device profile - 400: - description: If the YAML file is empty - 409: - description: If the profile's name matches an existing device profile. - 500: - description: For unknown or unanticipated issues - /v1/deviceprofile/uploadfile: - post: - description: Add a new device profile and associated command objects via YAML - profile file where name must be unique. - responses: - 200: - description: Database generated ID for the new device profile - 400: - description: If the YAML file is empty - 409: - description: If the profile's name matches an existing device profile. - 500: - description: for unknown or unanticipated issues - /v1/deviceprofile/yaml/name/{name}: - get: - description: Return, in yaml form, the device profiles matching given name where profile - names should be unique. May be null if no profiles matches on the name provided. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Device profile in YAML matching on name - content: - '*/*': - schema: - $ref: '#/components/schemas/deviceprofile' - 400: - description: For malformed or unparsable requests - 404: - description: If the device profile cannot be found by the name provided - 500: - description: For unknown or unanticipated issues - /v1/deviceprofile/yaml/{id}: - get: - description: Fetch the profile identified by database generated ID and return - as a YAML string. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Device profile in YAML format - content: - '*/*': - schema: - $ref: '#/components/schemas/deviceprofile' - 404: - description: if the device profile cannot be found by the ID provided - 500: - description: for unknown or unanticipated issues - /v1/deviceprofile/{id}: - get: - description: Fetch a specific profile by database generated ID. May return null - if no profile with the ID is found. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Device profile matching on ID - content: - '*/*': - schema: - $ref: '#/components/schemas/deviceprofile' - 404: - description: If the device profile cannot be found by the ID provided - 500: - description: For unknown or unanticipated issues - /v1/devicereport: - get: - description: Return all device reports sorted by ID. - responses: - 200: - description: List of device reports - content: - '*/*': - schema: - $ref: '#/components/schemas/devicereport' - 413: - description: If the number returned exceeds the max limit. - 500: - description: For unknown or unanticipated issues - put: - description: Update the device report identified by the ID or name in the object - provided. Id is used first, name is used second for identification purposes. - requestBody: - $ref: '#/components/requestBodies/devicereport' - responses: - 200: - description: Boolean on success of update request - 400: - description: For malformed or unparsable requests - 404: - description: If any referenced object cannot be found by its provided name - 500: - description: For unknown or unanticipated issues - post: - description: Add a new device report where name must be unique. - requestBody: - $ref: '#/components/requestBodies/devicereport' - responses: - 200: - description: Database generated ID for the new device report - 400: - description: For malformed or unparsable requests - 404: - description: If any referenced object cannot be found by its provided name - 500: - description: For unknown or unanticipated issues - /v1/devicereport/devicename/{devicename}: - get: - description: Return device reports with associated device matching given name - where device names should be unique. May be an empty list if no device matches - on the name provided. - parameters: - - name: devicename - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: device reports matching on device name - content: - '*/*': - schema: - $ref: '#/components/schemas/devicereport' - 400: - description: For malformed or unparsable requests - 500: - description: For unknown or unanticipated issues - /v1/devicereport/id/{id}: - delete: - description: Remove the device report designated by database generated ID. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of delete request - 404: - description: If no device report is found with the provided ID - 500: - description: For unknown or unanticipated issues - /v1/devicereport/name/{name}: - get: - description: Return device report matching given name where device report names should - be unique. May be null if no report matches on the name provided. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Device report matching on name - content: - '*/*': - schema: - $ref: '#/components/schemas/devicereport' - 400: - description: For malformed or unparsable requests - 404: - description: If no device report is found with the provided name - 500: - description: For unknown or unanticipated issues - delete: - description: Remove the device report designated by name. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of delete request - 400: - description: For malformed or unparsable requests - 404: - description: If no device report is found with the provided name - 500: - description: For unknown or unanticipated issues - /v1/devicereport/valueDescriptorsFor/{devicename}: - get: - description: Return list of value descriptor names associated with device reports - associated with name of the device provided. May be an empty list if no device - matches on the name provided. - parameters: - - name: devicename - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Value descriptor names - 400: - description: For malformed or unparsable requests - 500: - description: For unknown or unanticipated issues - /v1/devicereport/{id}: - get: - description: Fetch a specific device report by database generated ID. May return - null if no report with the ID is found. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Device report matching on the ID - content: - '*/*': - schema: - $ref: '#/components/schemas/devicereport' - 404: - description: If no device report is found with the provided ID - 500: - description: For unknown or unanticipated issues - /v1/deviceservice: - get: - description: Return all device services sorted by ID. - responses: - 200: - description: List of profiles - content: - '*/*': - schema: - $ref: '#/components/schemas/deviceservice' - 413: - description: If the number of device services exceeds the current max limit. - 500: - description: For unknown or unanticipated issues - put: - description: Update the device service identified by the ID or name stored in - the object provided.P 404) if the device service cannot - be found by the ID provided. - requestBody: - $ref: '#/components/requestBodies/deviceservice' - responses: - 200: - description: Boolean on success of update request - 400: - description: For malformed or unparsable requests - 404: - description: If no device service is found with the provided name or ID - 503: - description: For unknown or unanticipated issues - post: - description: Add a new device service where name must be unique. - requestBody: - $ref: '#/components/requestBodies/deviceservice' - responses: - 200: - description: Database generated ID for the new device service - 400: - description: No addressable was provided for the new device service - 404: - description: If an associated addressable (by ID or name) is not found - 409: - description: If the addressable name is determined to not be unique with - regard to others - 500: - description: For unknown or unanticipated issues - /v1/deviceservice/addressable/{addressableId}: - get: - description: Find all device servicess associated with the addressable with the - specified addressable database generated ID. List may be empty if - no device service match. - parameters: - - name: addressableId - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of device services associated with the addressable - content: - '*/*': - schema: - $ref: '#/components/schemas/deviceservice' - 404: - description: If no addressablee is found for the provided ID - 500: - description: For unknown or unanticipated issues - /v1/deviceservice/addressablename/{addressablename}: - get: - description: Find all device serices associated with the addressable with the - specified addressable name. List may be empty if no device services match. - parameters: - - name: addressablename - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of device services associated with the addressable - content: - '*/*': - schema: - $ref: '#/components/schemas/deviceservice' - 404: - description: If no addressable is found for the provided ID - 500: - description: For unknown or unanticipated issues - /v1/deviceservice/id/{id}: - delete: - description: Remove the device service designated by database generated ID. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of delete request - 404: - description: If no device service is found for the provided ID - 500: - description: For unknown or unanticipated issues - /v1/deviceservice/label/{label}: - get: - description: Find all device services having at least one label matching the - label provided. List may be empty if no device services match. - parameters: - - name: label - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of device service matching on specified label - content: - '*/*': - schema: - $ref: '#/components/schemas/deviceservice' - 400: - description: For malformed or unparsable requests - 500: - description: For unknown or unanticipated issues - /v1/deviceservice/name/{name}: - get: - description: Return the device service matching given name where service names should - be unique. May be null if no services matches on the name provided. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Device service matching on name - 400: - description: For malformed or unparsable requests - 404: - description: If no device service is found for the provided ID - 500: - description: For unknown or unanticipated issues - delete: - description: Remove the device service designated by name. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of delete request - 400: - description: For malformed or unparsable requests - 404: - description: If no device service is found for the provided ID - 503: - description: For unknown or unanticipated issues - /v1/deviceservice/name/{name}/adminstate/{adminState}: - put: - description: Update the admin state of the device service by device service - name. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: adminState - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of update request - 400: - description: For malformed or unparsable requests - 404: - description: If no device service is found for the provided ID - 500: - description: For unknown or unanticipated issues - /v1/deviceservice/name/{name}/lastconnected/{time}: - put: - description: Update the last connected time of the device service by unique - name of the device service. - parameters: - - name: time - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of update request - 400: - description: For malformed or unparsable requests - 404: - description: If no device service is found for the provided ID - 503: - description: For unknown or unanticipated issues - /v1/deviceservice/name/{name}/lastreported/{time}: - put: - description: Update the last reported time of the device service by unique name - of the device service. - parameters: - - name: time - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of update request - 400: - description: For malformed or unparsable requests - 404: - description: If no device service is found for the provided ID - 500: - description: For unknown or unanticipated issues - /v1/deviceservice/name/{name}/opstate/{opState}: - put: - description: Update the op status time of the device service by unique name - of the device service. - parameters: - - name: opState - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of update request - 400: - description: For malformed or unparsable requests - 404: - description: If no device service is found for the provided ID - 500: - description: For unknown or unanticipated issues - /v1/deviceservice/{id}: - get: - description: Fetch a specific device service by database generated ID. May return - null if no service with the ID is found - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Device service matching on ID - content: - '*/*': - schema: - $ref: '#/components/schemas/deviceservice' - 404: - description: If no device service is found for the provided ID - 500: - description: For unknown or unanticipated issues - /v1/deviceservice/{id}/adminstate/{adminState}: - put: - description: Update the admin state of the device service by database generated ID. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: adminState - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of update request - 400: - description: For malformed or unparsable requests - 404: - description: If no device service is found for the provided ID - 500: - description: For unknown or unanticipated issues - /v1/deviceservice/{id}/lastconnected/{time}: - put: - description: Update the last connected time of the device service by database - generated ID. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: time - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of update request - 400: - description: For malformed or unparsable requests - 404: - description: If no device service is found for the provided ID - 500: - description: For unknown or unanticipated issues - /v1/deviceservice/{id}/lastreported/{time}: - put: - description: Update the last reported time of the device service by database - generated ID. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: time - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of update request - 400: - description: For malformed or unparsable requests - 404: - description: If no device service is found for the provided ID - 503: - description: For unknown or unanticipated issues - /v1/deviceservice/{id}/opstate/{opState}: - put: - description: Update the op state of the device service by database generated ID. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: opState - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of update request - 400: - description: For malformed or unparsable requests - 404: - description: If no device service is found for the provided ID - 500: - description: For unknown or unanticipated issues - /v1/ping: - get: - description: ping - responses: - 200: - description: Successful Response - /v1/provisionwatcher: - get: - description: Return all provision watcher objects sorted by database generated ID. - responses: - 200: - description: List of provision watchers - content: - '*/*': - schema: - $ref: '#/components/schemas/provisionwatcher' - 413: - description: If the number returned exceeds the max limit. - 500: - description: For unknown or unanticipated issues. - put: - description: Update the provision watcher identified by the ID or name in the - object provided. - requestBody: - $ref: '#/components/requestBodies/provisionwatcher' - responses: - 200: - description: Boolean on success of update request - 400: - description: For malformed or unparsable requests - 404: - description: If no provision watcher with the provided ID is found. - 500: - description: For unknown or unanticipated issues or for any duplicate name - (key) error. - post: - description: Add a new provision watcher where name must be unique. - requestBody: - $ref: '#/components/requestBodies/provisionwatcher' - responses: - 200: - description: New database generated ID for the new provision watcher - 400: - description: For malformed or unparsable requests - 409: - description: If an associated object such as profile cannot be found - with the ID or name provided or if the watcher name is determined to not - be unique with regard to others - 500: - description: For unknown or unanticipated issues or for any duplicate name - (key) error. - /v1/provisionwatcher/{id}: - get: - description: Fetch a specific provision watcher by database generated ID. May - return null if no provision watcher matches on ID. - parameters: - - name: id - in: path - description: Database generated ID - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Provision watcher - content: - '*/*': - schema: - $ref: '#/components/schemas/provisionwatcher' - 404: - description: If no provision watcher with the provided ID is found. - 500: - description: For unknown or unanticipated issues - /v1/provisionwatcher/id/{id}: - delete: - description: Remove the provision watcher designated by the database generated - ID for the provision watcher. - parameters: - - name: id - in: path - description: Database generated ID - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of delete request - 404: - description: If no provision watcher with the provided ID is found. - 500: - description: For unknown or unanticipated issues - /v1/provisionwatcher/identifier/{key}/{value}: - get: - description: Find the provision watchers associated with the ID key/value - pair. - parameters: - - name: key - in: path - required: true - style: simple - explode: false - schema: - type: string - - name: value - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of provision watchers associated with the device service - content: - '*/*': - schema: - $ref: '#/components/schemas/provisionwatcher' - 400: - description: For malformed or unparsable requests - 500: - description: For unknown or unanticipated issues. - /v1/provisionwatcher/name/{name}: - get: - description: Return provision watcher with matching name where name should be unique. - May be null if none match. - parameters: - - name: name - in: path - description: Unique name of provision watcher - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Provision watcher matching on name - content: - '*/*': - schema: - $ref: '#/components/schemas/provisionwatcher' - 400: - description: For malformed or unparsable requests - 404: - description: If no provision watcher with the provided name is found. - 500: - description: For unknown or unanticipated issues. - delete: - description: Remove the provision watcher designated by unique name ID. - parameters: - - name: name - in: path - description: Unique name of provision watcher - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of delete request - 400: - description: For malformed or unparsable requests - 404: - description: If no provision watcher with the provided name is found. - 500: - description: For unknown or unanticipated issues. - /v1/provisionwatcher/profile/{profileId}: - get: - description: Find all provision watchers associated with the device profile with - the specified profile database generated ID. List may be empty if - no provision watchers match. - parameters: - - name: profileId - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of provision watchers associated with the device profile - content: - '*/*': - schema: - $ref: '#/components/schemas/provisionwatcher' - 404: - description: If no device profile match on the ID provided. - 500: - description: For unknown or unanticipated issues. - /v1/provisionwatcher/profilename/{profilename}: - get: - description: Find all provision watchers associated with the device profile with - the specified profile name. List may be empty if no provision watcher match. - parameters: - - name: profilename - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of provision watchers associated with the profile - content: - '*/*': - schema: - $ref: '#/components/schemas/provisionwatcher' - 400: - description: For malformed or unparsable requests - 404: - description: If no device profile match on the ID provided. - 500: - description: For unknown or unanticipated issues. - /v1/provisionwatcher/service/{serviceId}: - get: - description: Find the provision watchers associated with the device service with - the specified service database generated ID. List may be empty if - no provision watchers match. - parameters: - - name: serviceId - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of provision watchers associated with the device service - content: - '*/*': - schema: - $ref: '#/components/schemas/provisionwatcher' - 404: - description: If no device service match on the ID provided. - 500: - description: For unknown or unanticipated issues. - /v1/provisionwatcher/servicename/{servicename}: - get: - description: Find the provision watchers associated with the device service with - the specified service name. List may be none if no provision watcher match. - parameters: - - name: servicename - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: List of provision watchers associated with the device service - content: - '*/*': - schema: - $ref: '#/components/schemas/provisionwatcher' - 400: - description: For malformed or unparsable requests - 404: - description: If no device service match on the ID provided. - 500: - description: For unknown or unanticipated issues. - /version: - get: - description: Get the API version - responses: - 200: - description: The service's API version as JSON document -components: - schemas: - addressable: - title: addressable - required: - - method - type: object - properties: - address: - title: address - type: string - created: - title: created - type: integer - id: - title: id - type: string - method: - title: method - type: string - modified: - title: modified - type: integer - name: - title: name - type: string - origin: - title: origin - type: integer - password: - title: password - type: string - path: - title: path - type: string - port: - title: port - type: integer - protocol: - title: protocol - type: string - publisher: - title: publisher - type: string - topic: - title: topic - type: string - user: - title: user - type: string - autoevent: - title: autoevent - type: object - properties: - frequency: - title: frequency - type: string - onchange: - title: onchange - type: boolean - resource: - title: resource - type: string - command: - title: command - type: object - properties: - created: - title: created - type: integer - get: - $ref: '#/components/schemas/command_get' - id: - title: id - type: string - modified: - title: modified - type: integer - name: - title: name - type: string - origin: - title: origin - type: integer - put: - $ref: '#/components/schemas/command_put' - device: - title: device - type: object - properties: - adminState: - title: adminState - type: string - autoevents: - type: array - items: - title: autoevent - $ref: '#/components/schemas/autoevent' - service: - type: object - properties: - name: - title: name - type: string - profile: - type: object - properties: - name: - title: name - type: string - created: - title: created - type: integer - description: - title: description - type: string - id: - title: id - type: string - labels: - title: labels - uniqueItems: false - type: array - items: - title: labels - type: string - lastConnected: - title: lastConnected - type: integer - lastReported: - title: lastReported - type: integer - modified: - title: modified - type: integer - name: - title: name - type: string - operatingState: - title: operatingState - type: string - origin: - title: origin - type: integer - protocols: - type: object - additionalProperties: true - example: {"modbus-tcp":{"host":"localhost","port":"1234","unitID":"1"}} - description: Device or sensor supplying data and taking actuation commands - deviceprofile: - title: deviceprofile - type: object - properties: - created: - title: created - type: integer - description: - title: description - type: string - id: - title: id - type: string - labels: - title: labels - uniqueItems: false - type: array - items: - title: labels - type: string - manufacturer: - title: manufacturer - type: string - model: - title: model - type: string - modified: - title: modified - type: integer - name: - title: name - type: string - origin: - title: origin - type: integer - resources: - title: resources - uniqueItems: false - type: array - items: - title: resources - type: string - description: Template describing devices and sensors of the same nature in reporting - the same data and offering the same commands - devicereport: - title: devicereport - type: object - properties: - action: - title: action - type: string - created: - title: created - type: integer - device: - title: device - type: string - expected: - title: expected - uniqueItems: false - type: array - items: - title: expected - type: string - id: - title: id - type: string - modified: - title: modified - type: integer - name: - title: name - type: string - origin: - title: origin - type: integer - description: Description of values (value descriptors) that should be collected - per device on a interval action - deviceservice: - title: deviceservice - type: object - properties: - addressable: - $ref: '#/components/schemas/device_addressable' - adminState: - title: adminState - type: string - created: - title: created - type: integer - description: - title: description - type: string - id: - title: id - type: string - labels: - title: labels - uniqueItems: false - type: array - items: - title: labels - type: string - lastConnected: - title: lastConnected - type: integer - lastReported: - title: lastReported - type: integer - modified: - title: modified - type: integer - name: - title: name - type: string - operatingState: - title: operatingState - type: string - origin: - title: origin - type: integer - description: Manages devices and interfaces with core data - provisionwatcher: - title: provisionwatcher - type: object - properties: - created: - title: created - type: integer - id: - title: id - type: string - modified: - title: modified - type: integer - name: - title: name - type: string - origin: - title: origin - type: integer - command_get_responses: - type: object - properties: - code: - title: code - type: string - description: - title: description - type: string - expectedValues: - title: expectedValues - type: string - command_get: - type: object - properties: - responses: - $ref: '#/components/schemas/command_get_responses' - path: - title: path - type: string - command_put: - type: object - properties: - path: - title: path - type: string - device_addressable: - type: object - properties: - address: - title: address - type: string - created: - title: created - type: integer - id: - title: id - type: string - modified: - title: modified - type: integer - name: - title: name - type: string - origin: - title: origin - type: integer - password: - title: password - type: string - path: - title: path - type: string - port: - title: port - type: integer - protocol: - title: protocol - type: string - publisher: - title: publisher - type: string - topic: - title: topic - type: string - user: - title: user - type: string - requestBodies: - deviceservice: - content: - application/json: - schema: - $ref: '#/components/schemas/deviceservice' - required: true - device: - content: - application/json: - schema: - $ref: '#/components/schemas/device' - required: true - provisionwatcher: - content: - application/json: - schema: - $ref: '#/components/schemas/provisionwatcher' - required: true - addressable: - content: - application/json: - schema: - $ref: '#/components/schemas/addressable' - required: true - deviceprofile: - content: - application/json: - schema: - $ref: '#/components/schemas/deviceprofile' - required: true - devicereport: - content: - application/json: - schema: - $ref: '#/components/schemas/devicereport' - required: true diff --git a/openapi/v1/support-logging.yaml b/openapi/v1/support-logging.yaml deleted file mode 100644 index 869bf34e72..0000000000 --- a/openapi/v1/support-logging.yaml +++ /dev/null @@ -1,485 +0,0 @@ -openapi: 3.0.0 -info: - title: support-logging - version: 1.2.1 -servers: -- url: http://localhost:48061/api -paths: - /v1/config: - get: - description: Fetch the current state of the service's configuration. - responses: - 200: - description: The service's configuration as JSON document - 400: - description: Request is invalid or unparseable or if the - underlying configuration cannot be serialized to JSON properly. - /v1/logs: - post: - description: Create a new log entry - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/LogEntry' - required: true - responses: - 202: - description: Accepted to clients with timestamp being accepted. - 400: - description: Creation request is malformed - /v1/logs/keywords/{keywords}/{start}/{end}: - delete: - parameters: - - name: keywords - in: path - description: Accepting multiple keywords separated by comma - required: true - style: simple - explode: false - schema: - type: string - - name: start - in: path - description: Start date in long form - required: true - style: simple - explode: false - schema: - type: number - - name: end - in: path - description: End date in long form - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Count of the number of log entries being deleted - 500: - description: For unknown or unanticipated issues. - /v1/logs/keywords/{keywords}/{start}/{end}/{limit}: - get: - parameters: - - name: keywords - in: path - description: Accepting multiple keywords separated by comma - required: true - style: simple - explode: false - schema: - type: string - - name: start - in: path - description: Start date in long form - required: true - style: simple - explode: false - schema: - type: number - - name: end - in: path - description: End date in long form - required: true - style: simple - explode: false - schema: - type: number - - name: limit - in: path - description: The maximum number of records to fetch - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: List a collection of log entries whose message containing any - of the specified keywords and being created between the specified start - and end dates, limited in size by the limit parameter - 413: - description: If the number of events exceeds the current max limit - 500: - description: For unknown or unanticipated issues. - /v1/logs/logLevels/{logLevels}/originServices/{originServices}/{start}/{end}: - delete: - parameters: - - name: logLevels - in: path - description: Accepting multiple logLevels separated by comma - required: true - style: simple - explode: false - schema: - type: string - - name: originServices - in: path - description: Accepting multiple origin services separated by comma - required: true - style: simple - explode: false - schema: - type: string - - name: start - in: path - description: Start date in long form - required: true - style: simple - explode: false - schema: - type: number - - name: end - in: path - description: End date in long form - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Count of the number of log entries being deleted - 500: - description: For unknown or unanticipated issues. - /v1/logs/logLevels/{logLevels}/originServices/{originServices}/{start}/{end}/{limit}: - get: - parameters: - - name: logLevels - in: path - description: Accepting multiple logLevels separated by comma - required: true - style: simple - explode: false - schema: - type: string - - name: originServices - in: path - description: Accepting multiple origin services separated by comma - required: true - style: simple - explode: false - schema: - type: string - - name: start - in: path - description: Start date in long form - required: true - style: simple - explode: false - schema: - type: number - - name: end - in: path - description: End date in long form - required: true - style: simple - explode: false - schema: - type: number - - name: limit - in: path - description: The maximum number of records to fetch - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: List a collection of log entries matching any of the specified - logLevels, origin services, and also being created between the specified - start and end dates, limited in size by the limit parameter - 413: - description: If the number of events exceeds the current max limit - 500: - description: For unknown or unanticipated issues. - /v1/logs/logLevels/{logLevels}/{start}/{end}: - delete: - parameters: - - name: logLevels - in: path - description: Accepting multiple logLevels separated by comma - required: true - style: simple - explode: false - schema: - type: string - - name: start - in: path - description: Start date in long form - required: true - style: simple - explode: false - schema: - type: number - - name: end - in: path - description: End date in long form - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Count of the number of log entries being deleted - 500: - description: For unknown or unanticipated issues. - /v1/logs/logLevels/{logLevels}/{start}/{end}/{limit}: - get: - parameters: - - name: logLevels - in: path - description: Accepting multiple logLevels separated by comma - required: true - style: simple - explode: false - schema: - type: string - - name: start - in: path - description: Start date in long form - required: true - style: simple - explode: false - schema: - type: number - - name: end - in: path - description: End date in long form - required: true - style: simple - explode: false - schema: - type: number - - name: limit - in: path - description: The maximum number of records to fetch - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: List a collection of log entries matching any of the specified - logLevels and being created between the specified start and end dates, - limited in size by the limit parameter - 413: - description: If the number of events exceeds the current max limit - 500: - description: For unknown or unanticipated issues. - /v1/logs/originServices/{originServices}/{start}/{end}: - delete: - parameters: - - name: originServices - in: path - description: Accepting multiple origin services separated by comma - required: true - style: simple - explode: false - schema: - type: string - - name: start - in: path - description: Start date in long form - required: true - style: simple - explode: false - schema: - type: number - - name: end - in: path - description: End date in long form - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Count of the number of log entries being deleted - 500: - description: For unknown or unanticipated issues. - /v1/logs/originServices/{originServices}/{start}/{end}/{limit}: - get: - parameters: - - name: originServices - in: path - description: Accepting multiple origin services separated by comma - required: true - style: simple - explode: false - schema: - type: string - - name: start - in: path - description: Start date in long form - required: true - style: simple - explode: false - schema: - type: number - - name: end - in: path - description: End date in long form - required: true - style: simple - explode: false - schema: - type: number - - name: limit - in: path - description: The maximum number of records to fetch - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: List a collection of log entries matching any of the specified - origin services and being created between the specified start and end - dates, limited in size by the limit parameter - 413: - description: if the number of events exceeds the current max limit - 500: - description: for unknown or unanticipated issues. - /v1/logs/removeold/age/{age}: - delete: - parameters: - - name: age - in: path - description: Minimum age in milliseconds from created timestamp that a log entry - should be in order to be removed - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Count of the number of log entries being deleted - 500: - description: For unknown or unanticipated issues. - /v1/logs/{limit}: - get: - parameters: - - name: limit - in: path - description: The maximum number of records to fetch - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: List a collection of log entries, limited in size by the limit - parameter - 413: - description: If the number of events exceeds the current max limit - 500: - description: For unknown or unanticipated issues. - /v1/logs/{start}/{end}: - delete: - parameters: - - name: start - in: path - description: Start date in long form - required: true - style: simple - explode: false - schema: - type: number - - name: end - in: path - description: End date in long form - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Count of the number of log entries being deleted - 503: - description: For unknown or unanticipated issues. - /v1/logs/{start}/{end}/{limit}: - get: - parameters: - - name: start - in: path - description: Start date in long form - required: true - style: simple - explode: false - schema: - type: number - - name: end - in: path - description: End date in long form - required: true - style: simple - explode: false - schema: - type: number - - name: limit - in: path - description: The maximimum number of records to fetch - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: List a collection of log entries created between the specified - start and end dates, limited in size by the limit parameter - 413: - description: If the number of events exceeds the current max limit - 500: - description: For unknown or unanticipated issues. - /v1/ping: - get: - description: Test service providing an indication that the service is available. - responses: - 200: - description: Return value of "pong" - 503: - description: For unknown or unanticipated issues - /version: - get: - description: Get the API version - responses: - 200: - description: The service's API version as JSON document -components: - schemas: - LogEntry: - title: LogEntry Schema - required: - - logLevel - - message - - originService - type: object - properties: - created: - minimum: 0 - type: integer - description: The creation timestamp - logLevel: - type: string - enum: - - TRACE - - DEBUG - - INFO - - WARN - - ERROR - message: - type: string - originService: - type: string diff --git a/openapi/v1/support-notifications.yaml b/openapi/v1/support-notifications.yaml deleted file mode 100644 index 0b6a1d40c6..0000000000 --- a/openapi/v1/support-notifications.yaml +++ /dev/null @@ -1,1325 +0,0 @@ -openapi: 3.0.0 -info: - title: support-notifications - version: 1.2.1 -servers: -- url: http://localhost:48060/api -paths: - /v1/config: - get: - description: Fetch the current state of the service's configuration. - responses: - 200: - description: The service's configuration as a JSON document - 400: - description: Request is either invalid, unparseable, or the - configuration cannot be serialized. - /cleanup: - delete: - description: Delete all the notifications if the current timestamp minus their - last modification timestamp is less than a default age setting, and the corresponding - transmissions will also be deleted. - responses: - 202: - description: Return 202 Accepted status code without content when receiving - the request. - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /cleanup/age/{age}: - delete: - description: Delete all the notifications if the current timestamp minus their - last modification timestamp is less than the age parameter, and the corresponding - transmissions will also be deleted. - parameters: - - name: age - in: path - description: Specify the age of the type name, where the format is in milliseconds. - required: true - style: simple - explode: false - schema: - type: number - responses: - 202: - description: Return a 202 Accepted status code without content when receiving - the request. - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/notification: - post: - description: Receive alerts or notifications. Notifications of any severity - level are processed / distributed immediately. - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/notification' - required: true - responses: - 202: - description: Indicates that the notification has been received. - 409: - description: The slug is duplicate. Try another one. - content: - text/plain: - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - text/plain: - schema: - $ref: '#/components/schemas/Error' - /v1/notification/{id}: - get: - description: Fetch a specific notification by database specified ID, returning - null if none are found. - parameters: - - name: id - in: path - description: database-generated ID - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Return a notification - content: - '*/*': - schema: - $ref: '#/components/schemas/notification' - 404: - description: A notification with the specified ID cannot be found. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/notification/age/{age}: - delete: - description: Delete the proccessed notifications if the current timestamp minus - their last modification timestamp is less than the age parameter. - parameters: - - name: age - in: path - description: Specify the age of notification, and the format is in milliseconds. - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Return true to represent success. - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/notification/end/{end}/{limit}: - get: - description: Query the notification by creation timestamp before end date. - parameters: - - name: limit - in: path - description: The maximum number of records to fetch. - required: true - style: simple - explode: false - schema: - type: number - - name: end - in: path - description: End date in long form. - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Return a list of notifications. - content: - '*/*': - schema: - $ref: '#/components/schemas/NotificationArray' - 413: - description: The assigned limit perameter exceeds the current max limit. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/notification/labels/{labels}/{limit}: - get: - description: Query the notification by labels matching any one of them. - parameters: - - name: limit - in: path - description: The maximum number of records to fetch. - required: true - style: simple - explode: false - schema: - type: number - - name: labels - in: path - description: Accept multiple labels separated by comma. - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Return a list of notifications. - content: - '*/*': - schema: - $ref: '#/components/schemas/NotificationArray' - 413: - description: The assigned limit perameter exceeds the current max limit. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/notification/new/{limit}: - get: - description: Fetch the unprocessed notification, where status = NEW. - parameters: - - name: limit - in: path - description: The maximum number of records to fetch. - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Return a list of notifications. - content: - '*/*': - schema: - $ref: '#/components/schemas/NotificationArray' - 413: - description: The assigned limit perameter exceeds the current max limit. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/notification/sender/{sender}/{limit}: - get: - description: Query the notification by sender name with limited returned records. - parameters: - - name: limit - in: path - description: The maximum number of records to fetch. - required: true - style: simple - explode: false - schema: - type: number - - name: sender - in: path - description: The sender name of the subscription, which could be partially - matched, and is case insensitive for query. - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Return a list of notifications. - content: - '*/*': - schema: - $ref: '#/components/schemas/NotificationArray' - 413: - description: The assigned limit perameter exceeds the current max limit. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/notification/slug/{slug}: - get: - description: Query a specific notification by slug. - parameters: - - name: slug - in: path - description: Slug is a meaningful identifier provided by client, and is case - insensitive for query. - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Return a notification - content: - '*/*': - schema: - $ref: '#/components/schemas/notification' - 404: - description: The targeted resource is not found. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - delete: - description: Delete a specific notification by slug. - parameters: - - name: slug - in: path - description: Slug is a meaningful identifier provided by client, and is case - insensitive for query. - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Return true to represent success. - 404: - description: The targeted resource is not found. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/notification/start/{start}/end/{end}/{limit}: - get: - description: Query the notification by creation timestamp between start date - and end date. - parameters: - - name: limit - in: path - description: The maximum number of records to fetch. - required: true - style: simple - explode: false - schema: - type: number - - name: start - in: path - description: Start date in long form. - required: true - style: simple - explode: false - schema: - type: number - - name: end - in: path - description: End date in long form. - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Return a list of notifications. - content: - '*/*': - schema: - $ref: '#/components/schemas/NotificationArray' - 413: - description: The assigned limit perameter exceeds the current max limit. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/notification/start/{start}/{limit}: - get: - description: Query the notification by creation timestamp after start date. - parameters: - - name: limit - in: path - description: The maximum number of records to fetch. - required: true - style: simple - explode: false - schema: - type: number - - name: start - in: path - description: Start date in long form. - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Return a list of notifications. - content: - '*/*': - schema: - $ref: '#/components/schemas/NotificationArray' - 413: - description: The assigned limit perameter exceeds the current max limit. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/ping: - get: - description: Test service providing an indication that the service is available. - responses: - 200: - description: Return value of "pong." - 503: - description: For unanticipated or unknown issues encountered. - content: - text/plain: - schema: - $ref: '#/components/schemas/Error' - /v1/subscription: - get: - description: List all subscriptions. - responses: - 200: - description: Return subscriptions. - content: - '*/*': - schema: - $ref: '#/components/schemas/SubscriptionArray' - put: - description: Update a specific subscription according to the slug in request - body, and the Boolean value "true" will be returned to indicate updating successfully. - requestBody: - $ref: '#/components/requestBodies/subscription' - responses: - 200: - description: Return true to represent success. - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - post: - description: Create a new Subscritpion. - requestBody: - $ref: '#/components/requestBodies/subscription' - responses: - 201: - description: Return the slug when the subscription has been created successfully. - 409: - description: The slug is duplicate. Try another one. - content: - text/plain: - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - text/plain: - schema: - $ref: '#/components/schemas/Error' - /v1/subscription/categories/{categories}: - get: - description: Query the subscription by subscribed categories matching any one - of them. - parameters: - - name: categories - in: path - description: The subscribed categories, accepting multiple categories separated - by comma. - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Return subscriptions. - content: - '*/*': - schema: - $ref: '#/components/schemas/SubscriptionArray' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/subscription/categories/{categories}/labels/{labels}: - get: - description: Query the subscription by subscribed categories and labels matching - any one of them. - parameters: - - name: categories - in: path - description: The subscribed categories, accepting multiple categories separated - by comma. - required: true - style: simple - explode: false - schema: - type: string - - name: labels - in: path - description: The subscribed labels, accepting multiple labels separated by - comma. - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Return subscriptions. - content: - '*/*': - schema: - $ref: '#/components/schemas/SubscriptionArray' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/subscription/labels/{labels}: - get: - description: Query the subscription by subscribed labels matching any one of - them. - parameters: - - name: labels - in: path - description: The subscribed labels, accepting multiple labels separated by - comma. - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Return subscriptions. - content: - '*/*': - schema: - $ref: '#/components/schemas/SubscriptionArray' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/subscription/receiver/{receiver}: - get: - description: Query the subscriptions by Receiver Name. - parameters: - - name: receiver - in: path - description: The receiver name of the subscription, which could be partially - matched, and is case insensitive for query. - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Return subscriptions. - content: - '*/*': - schema: - $ref: '#/components/schemas/SubscriptionArray' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/subscription/slug/{slug}: - get: - description: Query a specific subscription by slug. - parameters: - - name: slug - in: path - description: Slug is a meaningful identifier provided by client, and is case - insensitive for query. - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Return a subscription. - content: - '*/*': - schema: - $ref: '#/components/schemas/subscription' - 404: - description: The targeted resource is not found. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - delete: - description: Delete a specific subscription by slug. - parameters: - - name: slug - in: path - description: Slug is a meaningful identifier provided by client, and is case - insensitive for query. - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Return true to represent success. - 404: - description: The targeted resource is not found. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/subscription/{id}: - get: - description: Fetch a specific subscription by database specified ID, returning - null if none are found. - issues - parameters: - - name: id - in: path - description: database-generated ID - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: subscription - 404: - description: If the subscription cannot be found by ID. - 500: - description: For unknown or unanticipated issues. - delete: - description: Delete a subscription given its database-generated ID. - parameters: - - name: id - in: path - description: Database-generated ID - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean on success of deletion request - 404: - description: If the subscription cannot be found by ID. - 500: - description: For unknown or unanticipated issues. - /v1/transmission/acknowledged/age/{age}: - delete: - description: Delete all the acknowledged transmissions, where status = ACKNOWLEDGED - if the current timestamp minus their last modification timestamp is less than - the age parameter. - parameters: - - name: age - in: path - description: Specify the age of transmission, and the format is in milliseconds. - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Return true to represent success. - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/transmission/end/{end}/{limit}: - get: - description: Query the transmissions by creation timestamp before end date. - parameters: - - name: limit - in: path - description: The maximum number of records to fetch. - required: true - style: simple - explode: false - schema: - type: number - - name: end - in: path - description: End date in long form. - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Return transmissions. - content: - '*/*': - schema: - $ref: '#/components/schemas/TransmissionArray' - 413: - description: The assigned limit perameter exceeds the current max limit. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/transmission/escalated/age/{age}: - delete: - description: Delete all the escalated transmissions, where status = ESCALATED if - the current timestamp minus their last modification timestamp is less than - the age parameter. - parameters: - - name: age - in: path - description: Specify the age of transmission, and the format is in milliseconds. - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Return true to represent success. - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/transmission/escalated/{limit}: - get: - description: Query the escalated transmissions, where status = ESCALATED - parameters: - - name: limit - in: path - description: The maximum number of records to fetch. - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Return transmissions. - content: - '*/*': - schema: - $ref: '#/components/schemas/TransmissionArray' - 413: - description: The assigned limit perameter exceeds the current max limit. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/transmission/failed/age/{age}: - delete: - description: Delete all the failed transmissions, where status = FAILED and resendCount - >= resend limit if the current timestamp minus their last modification timestamp - is less than the age parameter. - parameters: - - name: age - in: path - description: Specify the age of transmission, and the format is in milliseconds. - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Return true to represent success. - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/transmission/failed/{limit}: - get: - description: Query the failed transmissions, where status = FAILED - parameters: - - name: limit - in: path - description: The maximum number of records to fetch. - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Return transmissions. - content: - '*/*': - schema: - $ref: '#/components/schemas/TransmissionArray' - 413: - description: The assigned limit perameter exceeds the current max limit. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/transmission/sent/age/{age}: - delete: - description: Delete all the sent transmissions, where status = SENT if the current - timestamp minus their last modification timestamp is less than the age parameter. - parameters: - - name: age - in: path - description: Specify the age of transmission, and the format is in milliseconds. - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Return true to represent success. - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/transmission/slug/{slug}/start/{start}/end/{end}/{limit}: - get: - description: Query limited transmissions with specified slug and created date - range - parameters: - - name: limit - in: path - description: The maximum number of records to fetch. - required: true - style: simple - explode: false - schema: - type: number - - name: slug - in: path - description: This is a notification slug which is a meaningful identifier - provided by client, and it is case insensitive for query. - required: true - style: simple - explode: false - schema: - type: string - - name: start - in: path - description: Start date in long form. - required: true - style: simple - explode: false - schema: - type: number - - name: end - in: path - description: End date in long form. - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: List of transmissions - 400: - description: Request is invalid or unparseable - 413: - description: The assigned limit perameter exceeds the current max limit. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 500: - description: For unknown or unanticipated issues. - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/transmission/slug/{slug}/{limit}: - get: - description: Query the transmissions associating a specific notification by - the notification slug. - parameters: - - name: limit - in: path - description: The maximum number of records to fetch. - required: true - style: simple - explode: false - schema: - type: number - - name: slug - in: path - description: This is a notification slug which is a meaningful identifier - provided by client, and it is case insensitive for query. - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Return transmissions. - content: - '*/*': - schema: - $ref: '#/components/schemas/TransmissionArray' - 413: - description: The assigned limit perameter exceeds the current max limit. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/transmission/start/{start}/end/{end}/{limit}: - get: - description: Query the transmissions by creation timestamp between start date - and end date. - parameters: - - name: limit - in: path - description: The maximum number of records to fetch. - required: true - style: simple - explode: false - schema: - type: number - - name: start - in: path - description: Start date in long form. - required: true - style: simple - explode: false - schema: - type: number - - name: end - in: path - description: End date in long form. - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Return transmissions. - content: - '*/*': - schema: - $ref: '#/components/schemas/TransmissionArray' - 413: - description: The assigned limit perameter exceeds the current max limit. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /v1/transmission/start/{start}/{limit}: - get: - description: Query the transmissions by creation timestamp after start date. - parameters: - - name: limit - in: path - description: The maximum number of records to fetch. - required: true - style: simple - explode: false - schema: - type: number - - name: start - in: path - description: Start date in long form. - required: true - style: simple - explode: false - schema: - type: number - responses: - 200: - description: Return transmissions. - content: - '*/*': - schema: - $ref: '#/components/schemas/TransmissionArray' - 413: - description: The assigned limit perameter exceeds the current max limit. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - 503: - description: For unanticipated or unknown issues encountered. - content: - '*/*': - schema: - $ref: '#/components/schemas/Error' - /version: - get: - description: Get the API version - responses: - 200: - description: The service's API version as JSON document -components: - schemas: - Error: - title: Error Schema - required: - - error - - status - - timestamp - type: object - properties: - error: - type: string - exception: - type: string - description: The exception class in the code - message: - type: string - path: - type: string - status: - type: integer - description: HTTP status code - timestamp: - type: integer - notification: - title: notification Schema - required: - - category - - content - - sender - - severity - - slug - type: object - properties: - category: - type: string - enum: - - SECURITY - - HW_HEALTH - - SW_HEALTH - content: - type: string - created: - minimum: 0 - type: integer - description: The creation timestamp - description: - type: string - id: - type: string - description: Generated and used by system, and users can ignore this property - labels: - uniqueItems: true - type: array - items: - type: string - modified: - minimum: 0 - type: integer - description: The last modification timestamp - sender: - type: string - severity: - type: string - enum: - - CRITICAL - - NORMAL - slug: - type: string - description: A meaningful identifier provided by client - status: - type: string - enum: - - NEW - - PROCESSED - - ESCALATED - NotificationArray: - title: The array of notifications - type: array - items: - $ref: '#/components/schemas/notification' - subscription: - title: subscription Schema - required: - - channels - - receiver - - slug - type: object - properties: - channels: - uniqueItems: true - type: array - items: - type: object - anyOf: - - $ref: '#/components/schemas/RESTfulChannel' - - $ref: '#/components/schemas/EMAILChannel' - created: - minimum: 0 - type: integer - description: The creation timestamp - description: - type: string - id: - type: string - modified: - minimum: 0 - type: integer - description: The last modification timestamp - receiver: - type: string - slug: - type: string - description: A meaningful identifier provided by client - subscribedCategories: - uniqueItems: true - type: array - items: - type: string - enum: - - SECURITY - - HW_HEALTH - - SW_HEALTH - subscribedLabels: - uniqueItems: true - type: array - items: - type: string - SubscriptionArray: - title: The array of subscriptions - type: array - items: - $ref: '#/components/schemas/subscription' - transmission: - title: transmission Schema - required: - - channel - - notification - - receiver - - records - - resendCount - - status - type: object - properties: - channel: - type: object - oneOf: - - $ref: '#/components/schemas/RESTfulChannel' - - $ref: '#/components/schemas/EMAILChannel' - created: - minimum: 0 - type: integer - description: The creation timestamp - id: - type: string - modified: - minimum: 0 - type: integer - description: The last modification timestamp - notification: - $ref: '#/components/schemas/notification' - receiver: - type: string - records: - minItems: 1 - uniqueItems: true - type: array - items: - $ref: '#/components/schemas/TransmissionRecord' - resendCount: - minimum: 0 - type: integer - status: - type: string - enum: - - FAILED - - SENT - - ACKNOWLEDGED - - TRXESCALATED - TransmissionArray: - title: The array of transmissions - type: array - items: - $ref: '#/components/schemas/transmission' - EMAILChannel: - required: - - mailAddresses - - type - type: object - properties: - mailAddresses: - minItems: 1 - uniqueItems: true - type: array - items: - type: string - type: - type: string - enum: - - EMAIL - RESTfulChannel: - required: - - type - - url - type: object - properties: - contentType: - type: string - httpMethod: - type: string - enum: - - POST - - PUT - type: - type: string - enum: - - REST - url: - type: string - TransmissionRecord: - required: - - sent - - status - type: object - properties: - response: - type: string - sent: - minimum: 0 - type: integer - description: The sending timestamp - status: - type: string - enum: - - FAILED - - SENT - - ACKNOWLEDGED - requestBodies: - subscription: - content: - application/json: - schema: - $ref: '#/components/schemas/subscription' - required: true diff --git a/openapi/v1/support-scheduler.yaml b/openapi/v1/support-scheduler.yaml deleted file mode 100644 index af60ba74f4..0000000000 --- a/openapi/v1/support-scheduler.yaml +++ /dev/null @@ -1,445 +0,0 @@ -openapi: 3.0.0 -info: - title: support-scheduler - version: 1.2.1 -servers: -- url: http://localhost:48085/api -paths: - /v1/config: - get: - description: Fetch the current state of the service's configuration. - responses: - 200: - description: The service's configuration as JSON document - 400: - description: Request is invalid or unparseable or if the - underlying configuration cannot be serialized to JSON properly. - /v1/interval: - get: - description: Return all intervals sorted by ID. This interval's information - consists of the created and modified timestamps, along with the relevant ID, - name, start, frequency. May return nil if no interval with the ID is found. - responses: - 200: - description: List of intervals - content: - '*/*': - schema: - $ref: '#/components/schemas/interval' - 400: - description: For malformed or unparsable requests - 413: - description: If the number of intervals exceeds the current max limit. - 500: - description: For unknown or unanticipated issues - put: - description: Update an interval identified by the ID or name in the object provided. - ID is used first, name is used second for identification purposes. - requestBody: - $ref: '#/components/requestBodies/interval' - responses: - 200: - description: Boolean indicating success of the update - 400: - description: For malformed or unparsable requests - 409: - description: If the start, end, or frequency strings are not properly formatted - 500: - description: For unknown or unanticipated issues - post: - description: Add a new interval - name must be unique. Returns Internal Service - Error (HTTP 500) for unknown or unanticipated issues. DataValidationException - (HTTP 409) if the cron expression string is not properly formatted. - requestBody: - $ref: '#/components/requestBodies/interval' - responses: - 200: - description: Database-generated identifier for the new interval - 400: - description: For malformed or unparsable requests - 409: - description: If the start, end, or frequency strings are not properly formatted - or if the name is determined to not be unique with regard to others - 500: - description: For unknown or unanticipated issues - /v1/interval/name/{name}: - get: - description: Return an interval matching given, unique name. This interval's - information consists of the created and modified timestamps, along with - the relevant ID, name, start, frequency. May be nil if no interval matches - on the name provided. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Interval matching on name - content: - '*/*': - schema: - $ref: '#/components/schemas/interval' - 400: - description: For malformed or unparsable requests - 404: - description: If no interval is found for the identifier provided. - 500: - description: For unknown or unanticipated issues - delete: - description: Remove the interval designated by name. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean indicating success of the remove operation - 400: - description: For malformed or unparsable requests - 404: - description: If no interval is found for the name provided. - 500: - description: For unknown or unanticipated issues - /v1/interval/{id}: - get: - description: Fetch a specific interval by database generated ID. This information - consists of the created and modified timestamps, along with the relevant ID, - name, start, frequency. May return nil if no interval with the ID is found. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Interval matching on the ID - content: - '*/*': - schema: - $ref: '#/components/schemas/interval' - 404: - description: If no interval is found for the identifier provided. - 500: - description: For unknown or unanticipated issues - delete: - description: Remove the interval designated by database generated ID. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean indicating success of the remove operation - 404: - description: If no interval is found for the identifier provided. - 503: - description: For unknown or unanticipated issues - /v1/intervalaction: - get: - description: Return all interval events sorted by ID. The interval events information - consists of the ID, created, modified, name, interval, target, protocol, httpMethod, - address, port. - responses: - 200: - description: List of interval events - 413: - description: If the number of intervals exceeds the current max limit. - 500: - description: For unknown or unanticipated issues - put: - description: Update the IntervalAction identified by the ID or name in the object - provided. ID is used first, name is used second for identification purposes. - requestBody: - $ref: '#/components/requestBodies/intervalAction' - responses: - 200: - description: Boolean indicating success of the update - 400: - description: For malformed or unparsable requests - 404: - description: If no interval is found for the identifier provided. - 409: - description: If an attempt to change the name is made when the interval - action is still being referenced by device reports - 500: - description: For unknown or unanticipated issues - post: - description: Add a new IntervalAction for a given, unique IntervalAction. - requestBody: - $ref: '#/components/requestBodies/intervalAction' - responses: - 200: - description: Database generated identifier for the new interval - 400: - description: For malformed or unparsable requests - 404: - description: If the action's associated interval is not found (referenced - by name) - 409: - description: If the interval was not provided or if the name is determined - to not be unique with regard to others - 500: - description: for unknown or unanticipated issues or if interval action name - is a duplicate - /v1/intervalaction/name/{name}: - get: - description: Return interval events matching given, unique name. The interval - events information consists of the ID, created, modified, name, interval, - target, protocol, httpMethod, address, port. May be nil if no interval - events matches on the name provided. - name. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Interval action matching on name - 400: - description: For malformed or unparsable requests - 404: - description: If no IntervalAction is found with the provided name - 500: - description: For unknown or unanticipated issues - delete: - description: Remove the IntervalAction designated by name. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean indicating success of the remove operation - 400: - description: For malformed or unparsable requests - 404: - description: If no IntervalAction is found with the provided name - 409: - description: If an attempt to delete a interval action still being referenced - by device reports - 500: - description: for unknown or unanticipated issues - /v1/intervalaction/target/{name}: - get: - description: Return interval events matching given, unique name. The interval - events information consists of the ID, created, modified, name, interval, - target, protocol, httpMethod, address, port. May be nil if no interval - events matches on the name provided. - name. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Interval action matching on name - 400: - description: For malformed or unparsable requests - 404: - description: If no IntervalAction is found with the provided name - 500: - description: For unknown or unanticipated issues - delete: - description: Remove the IntervalAction(s) designated by name. - parameters: - - name: name - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean indicating success of the remove operation - 400: - description: For malformed or unparsable requests - 404: - description: If no IntervalAction is found with the provided name - 409: - description: If an attempt to delete a interval action still being referenced - by device reports - 500: - description: for unknown or unanticipated issues - /v1/intervalaction/{id}: - get: - description: Fetch a specific set of interval events matching given, unique - name. The interval events information consists of the ID, created, - modified, name, interval, target, protocol, httpMethod, address, - port. May return nil if no interval action with the ID is found. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Interval action matching on the ID - 404: - description: If no IntervalAction is found with the provided ID - 500: - description: For unknown or unanticipated issues - delete: - description: Remove the IntervalAction designated by database generated ID. - parameters: - - name: id - in: path - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: Boolean indicating success of the remove operation - 404: - description: If no IntervalAction is found with the provided ID - 409: - description: If an attempt to delete a interval action still being referenced - by device reports - 503: - description: For unknown or unanticipated issues - /v1/ping: - get: - description: ping - responses: - 200: - description: Successful Response - /version: - get: - description: Get the API version - responses: - 200: - description: The service's API version as JSON document -components: - schemas: - interval: - title: interval - type: object - properties: - created: - title: created - type: integer - end: - title: end - type: integer - frequency: - title: frequency - type: integer - id: - title: id - type: string - modified: - title: modified - type: integer - name: - title: name - type: string - origin: - title: origin - type: integer - start: - title: start - type: integer - description: meta data around anything that needs to be scheduled (frequency - with optional start and end times). - intervalAction: - title: intervalAction - required: - - target - type: object - properties: - parameters: - title: parameters - type: string - address: - title: address - type: string - created: - title: created - type: integer - httpmethod: - title: httpmethod - type: string - id: - title: id - type: string - interval: - title: interval - type: string - modified: - title: modified - type: integer - name: - title: name - type: string - origin: - title: origin - type: integer - password: - title: password - type: string - path: - title: path - type: string - port: - title: port - type: integer - protocol: - title: protocol - type: string - publisher: - title: publisher - type: string - target: - title: target - type: string - topic: - title: topic - type: string - user: - title: user - type: string - requestBodies: - interval: - content: - application/json: - schema: - $ref: '#/components/schemas/interval' - required: true - intervalAction: - content: - application/json: - schema: - $ref: '#/components/schemas/intervalAction' - required: true diff --git a/openapi/v1/system-agent.yaml b/openapi/v1/system-agent.yaml deleted file mode 100644 index f66139a272..0000000000 --- a/openapi/v1/system-agent.yaml +++ /dev/null @@ -1,149 +0,0 @@ -openapi: 3.0.0 -info: - title: system-agent - version: 1.2.1 -servers: -- url: http://localhost:48090/api -paths: - /v1/config/{services}: - get: - description: Fetch the configuration of the specified EdgeX services by their - unique names. - parameters: - - name: services - in: path - description: A comma-separated list of EdgeX service names to query for - configuration. - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: A list of configuration data corresponding to each requested, - valid service. - content: - '*/*': - schema: - $ref: '#/components/schemas/config' - 500: - description: For unknown or unanticipated issues. - /v1/metrics/{services}: - get: - description: Fetch the operating performance metrics of the specified EdgeX - unique names. - parameters: - - name: services - in: path - description: A comma-separated list of EdgeX service names to query for - configuration. - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: A list of metrics data corresponding to each requested, - valid service. - content: - '*/*': - schema: - $ref: '#/components/schemas/metric' - 500: - description: For unknown or unanticipated issues. - /v1/health/{services}: - get: - description: Fetch the health of the specified EdgeX services by their - unique names. - parameters: - - name: services - in: path - description: A comma-separated list of EdgeX service names to query for - health. - required: true - style: simple - explode: false - schema: - type: string - responses: - 200: - description: A list of health data corresponding to each requested, - valid service. - content: - '*/*': - schema: - $ref: '#/components/schemas/health' - 500: - description: For unknown or unanticipated issues. - /v1/operation: - post: - description: Issue a start, stop or restart action to the specified services. HTTP - 500 for unknown or unanticipated issues. - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/operation' - required: true - responses: - 200: - description: A list of services and their executors. - content: - '*/*': - schema: - $ref: '#/components/schemas/operation' - 500: - description: For unknown or unanticipated issues. - /v1/ping: - get: - description: Test service providing an indication that the service is available. - responses: - 200: - description: Return value of "pong" - 503: - description: For unknown or unanticipated issues - /version: - get: - description: Get the API version - responses: - 200: - description: The service's API version as JSON document -components: - schemas: - config: - title: config - type: object - properties: - Config: - title: Config - type: string - Service: - title: Service - type: string - description: Service configuration - metric: - title: metric - type: object - properties: - Metrics: - title: Metrics - type: string - Service: - title: Service - type: string - description: Service metrics - operation: - title: operation - type: object - properties: - action: - title: Action - type: string - services: - uniqueItems: true - type: array - items: - type: string - description: Service operation