From 22073ea1b600807f2509c8183951a0979e0352e9 Mon Sep 17 00:00:00 2001 From: BjoernAtBosch Date: Fri, 23 Jun 2023 21:09:39 +0200 Subject: [PATCH 01/16] Add middleware abstraction --- .vscode/settings.json | 4 +- .../seat-adjuster/src/SeatAdjusterApp.cpp | 4 +- .../set-data-points/src/SetDataPointsApp.cpp | 7 +- sdk/include/sdk/Config.h | 31 +++++++ sdk/include/sdk/IPubSubClient.h | 7 +- sdk/include/sdk/Utils.h | 14 ++- .../sdk/grpc/VehicleDataBrokerClient.h | 11 ++- sdk/include/sdk/middleware/Middleware.h | 62 +++++++++++++ .../sdk/vdb/IVehicleDataBrokerClient.h | 4 +- sdk/src/CMakeLists.txt | 13 +-- sdk/src/sdk/Utils.cpp | 24 ++++- sdk/src/sdk/VehicleApp.cpp | 17 ++-- sdk/src/sdk/dapr/DaprSupport.cpp | 53 ----------- sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp | 43 ++++----- sdk/src/sdk/middleware/DaprMiddleware.cpp | 89 +++++++++++++++++++ sdk/src/sdk/middleware/DaprMiddleware.h | 38 ++++++++ sdk/src/sdk/middleware/Middleware.cpp | 62 +++++++++++++ sdk/src/sdk/middleware/NativeMiddleware.cpp | 56 ++++++++++++ sdk/src/sdk/middleware/NativeMiddleware.h | 37 ++++++++ sdk/src/sdk/pubsub/MqttPubSubClient.cpp | 23 ++++- 20 files changed, 485 insertions(+), 114 deletions(-) create mode 100644 sdk/include/sdk/Config.h create mode 100644 sdk/include/sdk/middleware/Middleware.h delete mode 100644 sdk/src/sdk/dapr/DaprSupport.cpp create mode 100644 sdk/src/sdk/middleware/DaprMiddleware.cpp create mode 100644 sdk/src/sdk/middleware/DaprMiddleware.h create mode 100644 sdk/src/sdk/middleware/Middleware.cpp create mode 100644 sdk/src/sdk/middleware/NativeMiddleware.cpp create mode 100644 sdk/src/sdk/middleware/NativeMiddleware.h diff --git a/.vscode/settings.json b/.vscode/settings.json index 443e76e3..0419da64 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -70,6 +70,8 @@ "typeinfo": "cpp", "valarray": "cpp", "variant": "cpp", - "*.inc": "cpp" + "*.inc": "cpp", + "regex": "cpp", + "shared_mutex": "cpp" } } diff --git a/examples/seat-adjuster/src/SeatAdjusterApp.cpp b/examples/seat-adjuster/src/SeatAdjusterApp.cpp index c3e675b2..ca5696a5 100644 --- a/examples/seat-adjuster/src/SeatAdjusterApp.cpp +++ b/examples/seat-adjuster/src/SeatAdjusterApp.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2022 Robert Bosch GmbH + * Copyright (c) 2022-2023 Robert Bosch GmbH * * This program and the accompanying materials are made available under the * terms of the Apache License, Version 2.0 which is available at @@ -40,7 +40,7 @@ const auto STATUS_FAIL = 1; SeatAdjusterApp::SeatAdjusterApp() : VehicleApp(velocitas::IVehicleDataBrokerClient::createInstance("vehicledatabroker"), - velocitas::IPubSubClient::createInstance("localhost:1883", "SeatAdjusterApp")) + velocitas::IPubSubClient::createInstance("SeatAdjusterApp")) , m_vehicleModel(std::make_shared()) {} void SeatAdjusterApp::onStart() { diff --git a/examples/set-data-points/src/SetDataPointsApp.cpp b/examples/set-data-points/src/SetDataPointsApp.cpp index fd1079e0..e8c90713 100644 --- a/examples/set-data-points/src/SetDataPointsApp.cpp +++ b/examples/set-data-points/src/SetDataPointsApp.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2022 Robert Bosch GmbH + * Copyright (c) 2022-2023 Robert Bosch GmbH * * This program and the accompanying materials are made available under the * terms of the Apache License, Version 2.0 which is available at @@ -30,9 +30,8 @@ namespace example { class SetDataPointsApp : public velocitas::VehicleApp { public: SetDataPointsApp() - : VehicleApp( - velocitas::IVehicleDataBrokerClient::createInstance("vehicledatabroker"), - velocitas::IPubSubClient::createInstance("localhost:1883", "SetDataPointsApp")) {} + : VehicleApp(velocitas::IVehicleDataBrokerClient::createInstance("vehicledatabroker"), + velocitas::IPubSubClient::createInstance("SetDataPointsApp")) {} void onStart() override { velocitas::logger().info("Setting data points!"); diff --git a/sdk/include/sdk/Config.h b/sdk/include/sdk/Config.h new file mode 100644 index 00000000..3e110996 --- /dev/null +++ b/sdk/include/sdk/Config.h @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2023 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef VEHICLE_APP_SDK_CONFIG_H +#define VEHICLE_APP_SDK_CONFIG_H + +#include "middleware/Middleware.h" + +namespace velocitas { + +class Config { +public: + static Middleware& getMiddleware() { return Middleware::getInstance(); } +}; + +} // namespace velocitas + +#endif // VEHICLE_APP_SDK_CONFIG_H diff --git a/sdk/include/sdk/IPubSubClient.h b/sdk/include/sdk/IPubSubClient.h index 1f921027..06b939b5 100644 --- a/sdk/include/sdk/IPubSubClient.h +++ b/sdk/include/sdk/IPubSubClient.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2022 Robert Bosch GmbH + * Copyright (c) 2022-2023 Robert Bosch GmbH * * This program and the accompanying materials are made available under the * terms of the Apache License, Version 2.0 which is available at @@ -30,8 +30,9 @@ namespace velocitas { */ class IPubSubClient { public: - static std::shared_ptr createInstance(const std::string& brokerUri, - const std::string& clientId); + static std::shared_ptr createInstance(const std::string& clientId); + [[deprecated]] static std::shared_ptr + createInstance(const std::string& brokerUri, const std::string& clientId); virtual ~IPubSubClient() = default; diff --git a/sdk/include/sdk/Utils.h b/sdk/include/sdk/Utils.h index 283bfe16..11eb712c 100644 --- a/sdk/include/sdk/Utils.h +++ b/sdk/include/sdk/Utils.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2022 Robert Bosch GmbH + * Copyright (c) 2022-2023 Robert Bosch GmbH * * This program and the accompanying materials are made available under the * terms of the Apache License, Version 2.0 which is available at @@ -22,12 +22,24 @@ namespace velocitas { +/** + * @brief Get the value of the specified environment variable + * + * @param varName Name of the environment variable + * @param defaultValue Default if variable is not set + * @return std::string containing the value of the variable or the default value + */ +std::string getEnvVar(const std::string& varName, const std::string& defaultValue = ""); + /** * @brief Provides utility methods for handling strings. * */ class StringUtils final { public: + static std::string toLower(const std::string& str); + static std::string toUpper(const std::string& str); + static std::string join(const std::vector& stringVector, const std::string& separator); diff --git a/sdk/include/sdk/grpc/VehicleDataBrokerClient.h b/sdk/include/sdk/grpc/VehicleDataBrokerClient.h index 1ed19301..cfba1dfb 100644 --- a/sdk/include/sdk/grpc/VehicleDataBrokerClient.h +++ b/sdk/include/sdk/grpc/VehicleDataBrokerClient.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2022 Robert Bosch GmbH + * Copyright (c) 2022-2023 Robert Bosch GmbH * * This program and the accompanying materials are made available under the * terms of the Apache License, Version 2.0 which is available at @@ -20,6 +20,7 @@ #include "sdk/vdb/IVehicleDataBrokerClient.h" #include +#include #include namespace velocitas { @@ -33,8 +34,8 @@ class BrokerAsyncGrpcFacade; */ class VehicleDataBrokerClient : public IVehicleDataBrokerClient { public: - explicit VehicleDataBrokerClient(const std::string& vdbAddress, std::string vdbAppId); - explicit VehicleDataBrokerClient(const std::string& vdbAppId); + explicit VehicleDataBrokerClient(const std::string& vdbAddress, std::string vdbServiceName); + explicit VehicleDataBrokerClient(const std::string& vdbserviceName); ~VehicleDataBrokerClient() override; @@ -52,11 +53,9 @@ class VehicleDataBrokerClient : public IVehicleDataBrokerClient { AsyncSubscriptionPtr_t subscribe(const std::string& query) override; private: - static std::string getVdbEndpointAddress(); - std::shared_ptr m_asyncBrokerFacade; - std::string m_vdbAppId; }; + } // namespace velocitas #endif // VEHICLE_APP_SDK_VEHICLEDATABROKERCLIENT_H diff --git a/sdk/include/sdk/middleware/Middleware.h b/sdk/include/sdk/middleware/Middleware.h new file mode 100644 index 00000000..353ea44b --- /dev/null +++ b/sdk/include/sdk/middleware/Middleware.h @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2023 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef VEHICLE_APP_SDK_MIDDLEWARE_MIDDLEWARE_H +#define VEHICLE_APP_SDK_MIDDLEWARE_MIDDLEWARE_H + +#include +#include +#include + +namespace velocitas { + +class Middleware { +public: + enum class Type { UNKNOWN, NATIVE, DAPR }; + + static Middleware& getInstance() { + static std::unique_ptr singleton = instantiate(); + return *singleton; + } + + virtual Type getType() const = 0; + + virtual void start() {} + virtual void waitUntilReady() {} + virtual void stop() {} + + virtual std::string getServiceLocation(const std::string& serviceName) const = 0; + using Metadata = std::unordered_map; + virtual Metadata getMetadata(const std::string& serviceName) const { + std::ignore = serviceName; + return Metadata{}; + } + +protected: + Middleware() = default; + +private: + static std::unique_ptr instantiate(); + + Middleware(const Middleware&) = delete; + Middleware(Middleware&&) = delete; + Middleware& operator=(const Middleware&) = delete; + Middleware& operator=(Middleware&&) = delete; +}; + +} // namespace velocitas + +#endif // VEHICLE_APP_SDK_MIDDLEWARE_MIDDLEWARE_H diff --git a/sdk/include/sdk/vdb/IVehicleDataBrokerClient.h b/sdk/include/sdk/vdb/IVehicleDataBrokerClient.h index f2b8d161..0e1e41f9 100644 --- a/sdk/include/sdk/vdb/IVehicleDataBrokerClient.h +++ b/sdk/include/sdk/vdb/IVehicleDataBrokerClient.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2022 Robert Bosch GmbH + * Copyright (c) 2022-2023 Robert Bosch GmbH * * This program and the accompanying materials are made available under the * terms of the Apache License, Version 2.0 which is available at @@ -77,7 +77,7 @@ class IVehicleDataBrokerClient { * * @return std::shared_ptr */ - static std::shared_ptr createInstance(const std::string& vdbAppId); + static std::shared_ptr createInstance(const std::string& serviceName); protected: IVehicleDataBrokerClient() = default; diff --git a/sdk/src/CMakeLists.txt b/sdk/src/CMakeLists.txt index c97b56f8..53f265cc 100644 --- a/sdk/src/CMakeLists.txt +++ b/sdk/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2022 Robert Bosch GmbH +# Copyright (c) 2022-2023 Robert Bosch GmbH # # This program and the accompanying materials are made available under the # terms of the Apache License, Version 2.0 which is available at @@ -15,10 +15,7 @@ set(TARGET_NAME "vehicle-app-sdk") add_library(${TARGET_NAME} - sdk/vdb/DataPointBatch.cpp - sdk/VehicleApp.cpp - sdk/pubsub/MqttPubSubClient.cpp sdk/Node.cpp sdk/QueryBuilder.cpp sdk/DataPoint.cpp @@ -28,12 +25,18 @@ add_library(${TARGET_NAME} sdk/Utils.cpp sdk/Logger.cpp - sdk/dapr/DaprSupport.cpp sdk/grpc/GrpcClient.cpp sdk/grpc/AsyncGrpcFacade.cpp sdk/grpc/BrokerAsyncGrpcFacade.cpp sdk/grpc/GrpcDataPointValueProvider.cpp sdk/grpc/VehicleDataBrokerClient.cpp + + sdk/middleware/Middleware.cpp + sdk/middleware/DaprMiddleware.cpp + sdk/middleware/NativeMiddleware.cpp + + sdk/pubsub/MqttPubSubClient.cpp + sdk/vdb/DataPointBatch.cpp ) include_directories( diff --git a/sdk/src/sdk/Utils.cpp b/sdk/src/sdk/Utils.cpp index 552a0846..a56f0d7a 100644 --- a/sdk/src/sdk/Utils.cpp +++ b/sdk/src/sdk/Utils.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2022 Robert Bosch GmbH + * Copyright (c) 2022-2023 Robert Bosch GmbH * * This program and the accompanying materials are made available under the * terms of the Apache License, Version 2.0 which is available at @@ -17,10 +17,32 @@ #include "sdk/Utils.h" #include +#include +#include #include namespace velocitas { +std::string getEnvVar(const std::string& varName, const std::string& defaultValue) { + auto* const value = ::getenv(varName.c_str()); + if (value != nullptr) { + return std::string(value); + } + return defaultValue; +} + +std::string StringUtils::toLower(const std::string& str) { + std::string result{str}; + std::transform(result.begin(), result.end(), result.begin(), ::tolower); + return result; +} + +std::string StringUtils::toUpper(const std::string& str) { + std::string result{str}; + std::transform(result.begin(), result.end(), result.begin(), ::toupper); + return result; +} + std::string StringUtils::join(const std::vector& stringVector, const std::string& separator) { std::ostringstream oss; diff --git a/sdk/src/sdk/VehicleApp.cpp b/sdk/src/sdk/VehicleApp.cpp index 96d3348e..8c34ce16 100644 --- a/sdk/src/sdk/VehicleApp.cpp +++ b/sdk/src/sdk/VehicleApp.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2022 Robert Bosch GmbH + * Copyright (c) 2022-2023 Robert Bosch GmbH * * This program and the accompanying materials are made available under the * terms of the Apache License, Version 2.0 which is available at @@ -16,10 +16,10 @@ #include "sdk/VehicleApp.h" +#include "sdk/Config.h" #include "sdk/IPubSubClient.h" #include "sdk/Logger.h" #include "sdk/VehicleModelContext.h" -#include "sdk/dapr/DaprSupport.h" #include "sdk/vdb/IVehicleDataBrokerClient.h" #include @@ -40,12 +40,11 @@ VehicleApp::VehicleApp(std::shared_ptr vdbClient, } void VehicleApp::run() { - m_pubSubClient->connect(); - logger().info("Running App..."); + Config::getMiddleware().start(); + Config::getMiddleware().waitUntilReady(); - dapr::waitForSidecar(); - + m_pubSubClient->connect(); onStart(); // TODO: Fix busy waiting @@ -55,10 +54,12 @@ void VehicleApp::run() { } void VehicleApp::stop() { - onStop(); + logger().info("Stopping App..."); + onStop(); m_pubSubClient->disconnect(); - logger().info("Stopping App..."); + + Config::getMiddleware().stop(); } AsyncSubscriptionPtr_t VehicleApp::subscribeToTopic(const std::string& topic) { diff --git a/sdk/src/sdk/dapr/DaprSupport.cpp b/sdk/src/sdk/dapr/DaprSupport.cpp deleted file mode 100644 index 68349a30..00000000 --- a/sdk/src/sdk/dapr/DaprSupport.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) 2022 Robert Bosch GmbH - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://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. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "sdk/dapr/DaprSupport.h" - -#include "sdk/Logger.h" - -#include -#include - -#include - -namespace velocitas::dapr { - -void waitForSidecar() { - auto* const envPort = std::getenv("DAPR_HTTP_PORT"); - if (envPort == nullptr) { - logger().info("dapr: env DAPR_HTTP_PORT not set. Continuing without dapr ..."); - } else { - logger().info("dapr: env DAPR_HTTP_PORT set. Waiting for sidecar ..."); - const auto targetPort = atoi(envPort); - for (auto success = false; !success;) { - try { - constexpr auto STATUS_NO_CONTENT{204}; - logger().info("dapr: Requesting side car health endpoint..."); - - const auto response = - cpr::Get(cpr::Url(fmt::format("http://localhost:{}/v1.0/healthz", targetPort))); - success = response.status_code == STATUS_NO_CONTENT; - logger().debug("dapr: Health endpoint returned status code: {}, {}", - response.status_code, response.text); - } catch (const std::exception& e) { - logger().debug("dapr: Exception occurred during health endpoint: {}", e.what()); - } - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - } -} - -} // namespace velocitas::dapr diff --git a/sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp b/sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp index 5a75b099..50372df4 100644 --- a/sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp +++ b/sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2022 Robert Bosch GmbH + * Copyright (c) 2022-2023 Robert Bosch GmbH * * This program and the accompanying materials are made available under the * terms of the Apache License, Version 2.0 which is available at @@ -16,15 +16,16 @@ #include "sdk/grpc/VehicleDataBrokerClient.h" +#include "sdk/Config.h" #include "sdk/DataPointValue.h" -#include "sdk/grpc/AsyncGrpcFacade.h" -#include "sdk/grpc/BrokerAsyncGrpcFacade.h" -#include "sdk/grpc/GrpcDataPointValueProvider.h" - #include "sdk/Exceptions.h" #include "sdk/Job.h" #include "sdk/Logger.h" +#include "sdk/grpc/AsyncGrpcFacade.h" +#include "sdk/grpc/BrokerAsyncGrpcFacade.h" +#include "sdk/grpc/GrpcDataPointValueProvider.h" + #include #include #include @@ -36,31 +37,23 @@ namespace velocitas { VehicleDataBrokerClient::VehicleDataBrokerClient(const std::string& vdbAddress, - std::string vdbAppId) - : m_vdbAppId(std::move(vdbAppId)) { + std::string vdbServiceName) { m_asyncBrokerFacade = std::make_shared( grpc::CreateChannel(vdbAddress, grpc::InsecureChannelCredentials())); - m_asyncBrokerFacade->setContextModifier( - [this](auto& context) { context.AddMetadata("dapr-app-id", m_vdbAppId); }); + Middleware::Metadata metadata = Config::getMiddleware().getMetadata(vdbServiceName); + m_asyncBrokerFacade->setContextModifier([metadata](auto& context) { + for (auto metadatum : metadata) { + context.AddMetadata(metadatum.first, metadatum.second); + } + }); } -VehicleDataBrokerClient::VehicleDataBrokerClient(const std::string& vdbAppId) - : VehicleDataBrokerClient(getVdbEndpointAddress(), vdbAppId) {} +VehicleDataBrokerClient::VehicleDataBrokerClient(const std::string& vdbServiceName) + : VehicleDataBrokerClient(Config::getMiddleware().getServiceLocation(vdbServiceName), + vdbServiceName) {} VehicleDataBrokerClient::~VehicleDataBrokerClient() {} -std::string VehicleDataBrokerClient::getVdbEndpointAddress() { - constexpr auto DEFAULT_DAPR_GRPC_PORT{52001}; - - auto daprGrpcPort = DEFAULT_DAPR_GRPC_PORT; - auto* const envPort = std::getenv("DAPR_GRPC_PORT"); - if (envPort != nullptr) { - daprGrpcPort = atoi(envPort); - } - - return fmt::format("localhost:{}", daprGrpcPort); -} - static sdv::databroker::v1::Datapoint_Failure mapToGrpcType(DataPointValue::Failure failure) { switch (failure) { case DataPointValue::Failure::INVALID_VALUE: @@ -315,8 +308,8 @@ VehicleDataBrokerClient::subscribe(const std::string& query) { } std::shared_ptr -IVehicleDataBrokerClient::createInstance(const std::string& vdbAppId) { - return std::make_shared(vdbAppId); +IVehicleDataBrokerClient::createInstance(const std::string& vdbServiceName) { + return std::make_shared(vdbServiceName); } } // namespace velocitas diff --git a/sdk/src/sdk/middleware/DaprMiddleware.cpp b/sdk/src/sdk/middleware/DaprMiddleware.cpp new file mode 100644 index 00000000..0b560efb --- /dev/null +++ b/sdk/src/sdk/middleware/DaprMiddleware.cpp @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2022-2023 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "DaprMiddleware.h" + +#include "sdk/Logger.h" +#include "sdk/Utils.h" +#include "sdk/dapr/DaprSupport.h" + +#include +#include + +#include +#include + +#include + +namespace velocitas { + +static const std::string ENV_DAPR_GRPC_PORT = "DAPR_GRPC_PORT"; +static const std::string ENV_DAPR_HTTP_PORT = "DAPR_HTTP_PORT"; + +static const std::string DAPR_APP_ID_KEY = "dapr-app-id"; + +namespace dapr { + +void waitForSidecar() { + auto sidecarHttpPort = getEnvVar(ENV_DAPR_HTTP_PORT); + if (sidecarHttpPort.empty()) { + logger().info("dapr: env {} not set. Continuing without sidecar health check ...", + ENV_DAPR_HTTP_PORT); + } else { + logger().info("dapr: env {} set. Waiting for sidecar at port {} ...", ENV_DAPR_HTTP_PORT, + sidecarHttpPort); + for (auto success = false; !success;) { + try { + constexpr auto STATUS_NO_CONTENT{204}; + logger().info("dapr: Requesting side car health endpoint..."); + + const auto response = cpr::Get( + cpr::Url(fmt::format("http://localhost:{}/v1.0/healthz", sidecarHttpPort))); + success = response.status_code == STATUS_NO_CONTENT; + logger().debug("dapr: Health endpoint returned status code: {}, {}", + response.status_code, response.text); + } catch (const std::exception& e) { + logger().debug("dapr: Exception occurred during health endpoint: {}", e.what()); + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } +} + +} // namespace dapr + +void DaprMiddleware::waitUntilReady() { dapr::waitForSidecar(); } + +std::string DaprMiddleware::getServiceLocation(const std::string& serviceName) const { + std::ignore = serviceName; + + auto daprSidecarGrpcPort = getEnvVar(ENV_DAPR_GRPC_PORT); + if (daprSidecarGrpcPort.empty()) { + throw std::runtime_error( + fmt::format("{} not set! Cannot connect to Dapr sidecar!", ENV_DAPR_GRPC_PORT)); + } + return "grpc:://localhost:" + daprSidecarGrpcPort; +}; + +Middleware::Metadata DaprMiddleware::getMetadata(const std::string& serviceName) const { + auto appId = getEnvVar(StringUtils::toUpper(serviceName) + "_DAPR_APP_ID"); + if (appId.empty()) { + appId = StringUtils::toLower(serviceName); + } + return {{DAPR_APP_ID_KEY, appId}}; +} + +} // namespace velocitas diff --git a/sdk/src/sdk/middleware/DaprMiddleware.h b/sdk/src/sdk/middleware/DaprMiddleware.h new file mode 100644 index 00000000..755f61c4 --- /dev/null +++ b/sdk/src/sdk/middleware/DaprMiddleware.h @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2023 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef VEHICLE_APP_SDK_MIDDLEWARE_DAPRMIDDLEWARE_H +#define VEHICLE_APP_SDK_MIDDLEWARE_DAPRMIDDLEWARE_H + +#include "sdk/middleware/Middleware.h" + +namespace velocitas { + +class DaprMiddleware : public Middleware { +public: + DaprMiddleware() = default; + + Type getType() const override { return Type::DAPR; } + + void waitUntilReady() override; + + std::string getServiceLocation(const std::string& serviceName) const override; + Metadata getMetadata(const std::string& serviceName) const override; +}; + +} // namespace velocitas + +#endif // VEHICLE_APP_SDK_MIDDLEWARE_DAPRMIDDLEWARE_H diff --git a/sdk/src/sdk/middleware/Middleware.cpp b/sdk/src/sdk/middleware/Middleware.cpp new file mode 100644 index 00000000..a845fb80 --- /dev/null +++ b/sdk/src/sdk/middleware/Middleware.cpp @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2023 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdk/middleware/Middleware.h" + +#include "DaprMiddleware.h" +#include "NativeMiddleware.h" + +#include "sdk/Utils.h" + +#include + +#include +#include +#include + +namespace velocitas { + +static const std::string ENV_MIDDLEWARE_TYPE{"SDV_MIDDLEWARE_TYPE"}; + +static const std::unordered_map MIDDLEWARE_NAME_TYPE_MAP = { + {"", Middleware::Type::DAPR}, // default + {"dapr", Middleware::Type::DAPR}, + {"native", Middleware::Type::NATIVE}, +}; + +static Middleware::Type getMiddlewareType(const std::string& middlewareName) { + auto iter = MIDDLEWARE_NAME_TYPE_MAP.find(StringUtils::toLower(middlewareName)); + if (iter != MIDDLEWARE_NAME_TYPE_MAP.end()) { + return iter->second; + } + return Middleware::Type::UNKNOWN; +} + +std::unique_ptr Middleware::instantiate() { + std::string middlewareName = getEnvVar(ENV_MIDDLEWARE_TYPE); + Middleware::Type middlewareType = getMiddlewareType(middlewareName); + switch (middlewareType) { + case Type::NATIVE: + return std::make_unique(); + case Type::DAPR: + return std::make_unique(); + default: + throw std::runtime_error(fmt::format("Unknown middleware type '{}' -> {}", middlewareName, + static_cast(middlewareType))); + } +} + +} // namespace velocitas diff --git a/sdk/src/sdk/middleware/NativeMiddleware.cpp b/sdk/src/sdk/middleware/NativeMiddleware.cpp new file mode 100644 index 00000000..ea691f7c --- /dev/null +++ b/sdk/src/sdk/middleware/NativeMiddleware.cpp @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2023 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "NativeMiddleware.h" + +#include "sdk/Logger.h" +#include "sdk/Utils.h" +#include "sdk/dapr/DaprSupport.h" + +#include +#include +#include + +namespace velocitas { + +static const std::unordered_map DEFAULT_LOCATIONS = { + {"mqtt", "mqtt://localhost:1883"}, + {"vehicledatabroker", "grpc://localhost:55555"}, +}; + +static std::string getDefaultLocation(const std::string& serviceName) { + std::string defaultLocation; + auto iter = DEFAULT_LOCATIONS.find(StringUtils::toLower(serviceName)); + if (iter != DEFAULT_LOCATIONS.end()) { + defaultLocation = iter->second; + } + return defaultLocation; +}; + +std::string NativeMiddleware::getServiceEnvVarName(const std::string& serviceName) const { + return "SDV_" + StringUtils::toUpper(serviceName) + "_ADDRESS"; +} + +std::string NativeMiddleware::getServiceLocation(const std::string& serviceName) const { + auto envVarName = getServiceEnvVarName(serviceName); + auto serviceAddress = getEnvVar(envVarName); + if (serviceAddress.empty()) { + serviceAddress = getDefaultLocation(serviceName); + } + return serviceAddress; +} + +} // namespace velocitas diff --git a/sdk/src/sdk/middleware/NativeMiddleware.h b/sdk/src/sdk/middleware/NativeMiddleware.h new file mode 100644 index 00000000..f68e7b72 --- /dev/null +++ b/sdk/src/sdk/middleware/NativeMiddleware.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2023 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef VEHICLE_APP_SDK_MIDDLEWARE_NATIVEMIDDLEWARE_H +#define VEHICLE_APP_SDK_MIDDLEWARE_NATIVEMIDDLEWARE_H + +#include "sdk/middleware/Middleware.h" + +namespace velocitas { + +class NativeMiddleware : public Middleware { +public: + NativeMiddleware() = default; + + Type getType() const override { return Type::NATIVE; } + + std::string getServiceEnvVarName(const std::string& serviceName) const; + + std::string getServiceLocation(const std::string& serviceName) const override; +}; + +} // namespace velocitas + +#endif // VEHICLE_APP_SDK_MIDDLEWARE_NATIVEMIDDLEWARE_H diff --git a/sdk/src/sdk/pubsub/MqttPubSubClient.cpp b/sdk/src/sdk/pubsub/MqttPubSubClient.cpp index 76a4b3aa..c54b8792 100644 --- a/sdk/src/sdk/pubsub/MqttPubSubClient.cpp +++ b/sdk/src/sdk/pubsub/MqttPubSubClient.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2022 Robert Bosch GmbH + * Copyright (c) 2022-2023 Robert Bosch GmbH * * This program and the accompanying materials are made available under the * terms of the Apache License, Version 2.0 which is available at @@ -14,11 +14,14 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include "sdk/Config.h" #include "sdk/IPubSubClient.h" #include "sdk/Logger.h" #include "sdk/Status.h" #include "sdk/ThreadPool.h" +#include "sdk/middleware/NativeMiddleware.h" + #include #include @@ -39,10 +42,14 @@ class MqttPubSubClient : public IPubSubClient, private mqtt::callback { ~MqttPubSubClient() override = default; void connect() override { - auto* const brokerUri = getenv("MQTT_BROKER_URI"); - auto optionsBuilder = mqtt::connect_options_builder(); + auto optionsBuilder = mqtt::connect_options_builder(); + auto* const brokerUri = getenv("MQTT_BROKER_URI"); if (brokerUri != nullptr) { + logger().warn("You're use deprecated environment variable MQTT_BROKER_URI. Support for " + "this will be removed soon.\n" + "Please switch to current variable {}.", + NativeMiddleware().getServiceEnvVarName("mqtt")); optionsBuilder.servers( std::make_shared(std::string{brokerUri})); } @@ -93,6 +100,16 @@ class MqttPubSubClient : public IPubSubClient, private mqtt::callback { TopicMap_t m_subscriberMap; }; +std::shared_ptr IPubSubClient::createInstance(const std::string& clientId) { + if (Config::getMiddleware().getType() == Middleware::Type::DAPR) { + logger().warn("Velocitas' C++ SDK does not yet support Dapr PubSub " + "-> connecting directly to MQTT broker!"); + } + + std::string brokerLocation = NativeMiddleware().getServiceLocation("mqtt"); + return std::make_shared(brokerLocation, clientId); +} + std::shared_ptr IPubSubClient::createInstance(const std::string& brokerUri, const std::string& clientId) { return std::make_shared(brokerUri, clientId); From cf5c26d4d3e5602a754898953e9d56ce6d15838d Mon Sep 17 00:00:00 2001 From: BjoernAtBosch Date: Mon, 26 Jun 2023 11:02:12 +0200 Subject: [PATCH 02/16] Fix VehicleDataBrokerClient_tests.cpp --- .../unit/VehicleDataBrokerClient_tests.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/sdk/tests/unit/VehicleDataBrokerClient_tests.cpp b/sdk/tests/unit/VehicleDataBrokerClient_tests.cpp index 68fd2db9..72ad07e3 100644 --- a/sdk/tests/unit/VehicleDataBrokerClient_tests.cpp +++ b/sdk/tests/unit/VehicleDataBrokerClient_tests.cpp @@ -18,9 +18,20 @@ #include +#include + using namespace velocitas; -TEST(Test_VehicleDataBrokerClient, getDatapoints_noConnection_throwsAsyncException) { - auto client = VehicleDataBrokerClient("my-app-id"); - EXPECT_THROW(client.getDatapoints({})->await(), AsyncException); +class Test_VehicleDataBrokerClient : public ::testing::Test { +protected: + static void SetUpTestSuite() { ::setenv("SDV_MIDDLEWARE_TYPE", "native", /*overwrite=*/true); } + + Test_VehicleDataBrokerClient() + : m_cut("some-broker-service-name") {} + + VehicleDataBrokerClient m_cut; +}; + +TEST_F(Test_VehicleDataBrokerClient, getDatapoints_noConnection_throwsAsyncException) { + EXPECT_THROW(m_cut.getDatapoints({})->await(), AsyncException); } From 58058f19087a294503df3ae972ec9c86edeeb451 Mon Sep 17 00:00:00 2001 From: BjoernAtBosch Date: Mon, 26 Jun 2023 12:18:24 +0200 Subject: [PATCH 03/16] Fix linter issues --- sdk/src/sdk/middleware/DaprMiddleware.cpp | 6 +++--- sdk/src/sdk/middleware/Middleware.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/src/sdk/middleware/DaprMiddleware.cpp b/sdk/src/sdk/middleware/DaprMiddleware.cpp index 0b560efb..b4afad96 100644 --- a/sdk/src/sdk/middleware/DaprMiddleware.cpp +++ b/sdk/src/sdk/middleware/DaprMiddleware.cpp @@ -30,10 +30,10 @@ namespace velocitas { -static const std::string ENV_DAPR_GRPC_PORT = "DAPR_GRPC_PORT"; -static const std::string ENV_DAPR_HTTP_PORT = "DAPR_HTTP_PORT"; +static constexpr char const* ENV_DAPR_GRPC_PORT = "DAPR_GRPC_PORT"; +static constexpr char const* ENV_DAPR_HTTP_PORT = "DAPR_HTTP_PORT"; -static const std::string DAPR_APP_ID_KEY = "dapr-app-id"; +static constexpr char const* DAPR_APP_ID_KEY = "dapr-app-id"; namespace dapr { diff --git a/sdk/src/sdk/middleware/Middleware.cpp b/sdk/src/sdk/middleware/Middleware.cpp index a845fb80..7822f56f 100644 --- a/sdk/src/sdk/middleware/Middleware.cpp +++ b/sdk/src/sdk/middleware/Middleware.cpp @@ -29,7 +29,7 @@ namespace velocitas { -static const std::string ENV_MIDDLEWARE_TYPE{"SDV_MIDDLEWARE_TYPE"}; +static constexpr char const* ENV_MIDDLEWARE_TYPE = "SDV_MIDDLEWARE_TYPE"; static const std::unordered_map MIDDLEWARE_NAME_TYPE_MAP = { {"", Middleware::Type::DAPR}, // default From 0e4f333a2504459b25d7a072b0e25a1ebce3f5b4 Mon Sep 17 00:00:00 2001 From: BjoernAtBosch Date: Tue, 27 Jun 2023 20:24:37 +0200 Subject: [PATCH 04/16] Add tests + use string as type-id --- sdk/include/sdk/IPubSubClient.h | 4 +- sdk/include/sdk/middleware/Middleware.h | 67 ++++++++++++++++++-- sdk/src/sdk/middleware/DaprMiddleware.cpp | 11 +++- sdk/src/sdk/middleware/DaprMiddleware.h | 6 +- sdk/src/sdk/middleware/Middleware.cpp | 38 +++++------- sdk/src/sdk/middleware/NativeMiddleware.cpp | 9 ++- sdk/src/sdk/middleware/NativeMiddleware.h | 8 +-- sdk/src/sdk/pubsub/MqttPubSubClient.cpp | 16 ++--- sdk/tests/unit/CMakeLists.txt | 10 ++- sdk/tests/unit/DaprMiddleware_tests.cpp | 69 +++++++++++++++++++++ sdk/tests/unit/Middleware_tests.cpp | 59 ++++++++++++++++++ sdk/tests/unit/NativeMiddleware_tests.cpp | 60 ++++++++++++++++++ sdk/tests/unit/Utils_tests.cpp | 42 +++++++++++++ sdk/tests/unit/testmain.cpp | 38 ++++++++++++ 14 files changed, 386 insertions(+), 51 deletions(-) create mode 100644 sdk/tests/unit/DaprMiddleware_tests.cpp create mode 100644 sdk/tests/unit/Middleware_tests.cpp create mode 100644 sdk/tests/unit/NativeMiddleware_tests.cpp create mode 100644 sdk/tests/unit/testmain.cpp diff --git a/sdk/include/sdk/IPubSubClient.h b/sdk/include/sdk/IPubSubClient.h index 06b939b5..73253528 100644 --- a/sdk/include/sdk/IPubSubClient.h +++ b/sdk/include/sdk/IPubSubClient.h @@ -31,8 +31,8 @@ namespace velocitas { class IPubSubClient { public: static std::shared_ptr createInstance(const std::string& clientId); - [[deprecated]] static std::shared_ptr - createInstance(const std::string& brokerUri, const std::string& clientId); + static std::shared_ptr createInstance(const std::string& brokerUri, + const std::string& clientId); virtual ~IPubSubClient() = default; diff --git a/sdk/include/sdk/middleware/Middleware.h b/sdk/include/sdk/middleware/Middleware.h index 353ea44b..7b531c58 100644 --- a/sdk/include/sdk/middleware/Middleware.h +++ b/sdk/include/sdk/middleware/Middleware.h @@ -23,34 +23,93 @@ namespace velocitas { +class IPubSubClient; + +/** + * @brief Abstract base class for concrete middleware implementations + */ class Middleware { public: - enum class Type { UNKNOWN, NATIVE, DAPR }; - + /** + * @brief Returns a reference to a singelton instance of a concrete middleware class + * + * @return Middleware& + */ static Middleware& getInstance() { static std::unique_ptr singleton = instantiate(); return *singleton; } - virtual Type getType() const = 0; + static std::string getTypeDefiningEnvVarName(); + + /** + * @brief Get the type identifier of the concrete middleware implementation + * + * @return std::string type of the middleware + */ + std::string getTypeId() const { return m_typeId; } + /** + * @brief Triggers the start of the middleware + */ virtual void start() {} + /** + * @brief Waits (blocks current thread) until the middleware is started and ready to use + */ virtual void waitUntilReady() {} + /** + * @brief Stops the middleware + */ virtual void stop() {} + /** + * @brief Get the location description (e.g. uri) of the specified service name + * + * @param serviceName Name of the service to get the loaction description for + * @return std::string representing the location description + */ virtual std::string getServiceLocation(const std::string& serviceName) const = 0; + + /** + * @brief Generic type for middleware specific metadata * + */ using Metadata = std::unordered_map; + + /** + * @brief Get the middleware specific metadata needed to communicate with the specified service. + * + * @param serviceName Name of the service to communicate with + * @return Metadata + */ virtual Metadata getMetadata(const std::string& serviceName) const { std::ignore = serviceName; return Metadata{}; } + /** + * @brief Create a Pub Sub Client object + * + * @param clientId + * @return std::shared_ptr + */ + virtual std::shared_ptr + createPubSubClient(const std::string& clientId) const = 0; + protected: - Middleware() = default; + /** + * @brief Constructor to be called by subclasses + * + * @param typeId unique identifier for the type of the concrete middleware + * implementation + */ + Middleware(std::string&& typeId) + : m_typeId(std::move(typeId)) {} private: static std::unique_ptr instantiate(); + const std::string m_typeId; + Middleware(const Middleware&) = delete; Middleware(Middleware&&) = delete; Middleware& operator=(const Middleware&) = delete; diff --git a/sdk/src/sdk/middleware/DaprMiddleware.cpp b/sdk/src/sdk/middleware/DaprMiddleware.cpp index b4afad96..23801556 100644 --- a/sdk/src/sdk/middleware/DaprMiddleware.cpp +++ b/sdk/src/sdk/middleware/DaprMiddleware.cpp @@ -16,6 +16,7 @@ #include "DaprMiddleware.h" +#include "NativeMiddleware.h" #include "sdk/Logger.h" #include "sdk/Utils.h" #include "sdk/dapr/DaprSupport.h" @@ -75,7 +76,7 @@ std::string DaprMiddleware::getServiceLocation(const std::string& serviceName) c throw std::runtime_error( fmt::format("{} not set! Cannot connect to Dapr sidecar!", ENV_DAPR_GRPC_PORT)); } - return "grpc:://localhost:" + daprSidecarGrpcPort; + return "grpc://localhost:" + daprSidecarGrpcPort; }; Middleware::Metadata DaprMiddleware::getMetadata(const std::string& serviceName) const { @@ -86,4 +87,12 @@ Middleware::Metadata DaprMiddleware::getMetadata(const std::string& serviceName) return {{DAPR_APP_ID_KEY, appId}}; } +std::shared_ptr +DaprMiddleware::createPubSubClient(const std::string& clientId) const { + logger().warn("Velocitas' C++ SDK does not yet support Dapr PubSub " + "-> connecting directly to MQTT broker!"); + + return NativeMiddleware().createPubSubClient(clientId); +} + } // namespace velocitas diff --git a/sdk/src/sdk/middleware/DaprMiddleware.h b/sdk/src/sdk/middleware/DaprMiddleware.h index 755f61c4..e0188c3f 100644 --- a/sdk/src/sdk/middleware/DaprMiddleware.h +++ b/sdk/src/sdk/middleware/DaprMiddleware.h @@ -23,14 +23,16 @@ namespace velocitas { class DaprMiddleware : public Middleware { public: - DaprMiddleware() = default; + static constexpr char const* TYPE_ID = "dapr"; - Type getType() const override { return Type::DAPR; } + DaprMiddleware() + : Middleware(TYPE_ID) {} void waitUntilReady() override; std::string getServiceLocation(const std::string& serviceName) const override; Metadata getMetadata(const std::string& serviceName) const override; + std::shared_ptr createPubSubClient(const std::string& clientId) const override; }; } // namespace velocitas diff --git a/sdk/src/sdk/middleware/Middleware.cpp b/sdk/src/sdk/middleware/Middleware.cpp index 7822f56f..95bdab30 100644 --- a/sdk/src/sdk/middleware/Middleware.cpp +++ b/sdk/src/sdk/middleware/Middleware.cpp @@ -29,33 +29,25 @@ namespace velocitas { -static constexpr char const* ENV_MIDDLEWARE_TYPE = "SDV_MIDDLEWARE_TYPE"; - -static const std::unordered_map MIDDLEWARE_NAME_TYPE_MAP = { - {"", Middleware::Type::DAPR}, // default - {"dapr", Middleware::Type::DAPR}, - {"native", Middleware::Type::NATIVE}, -}; - -static Middleware::Type getMiddlewareType(const std::string& middlewareName) { - auto iter = MIDDLEWARE_NAME_TYPE_MAP.find(StringUtils::toLower(middlewareName)); - if (iter != MIDDLEWARE_NAME_TYPE_MAP.end()) { - return iter->second; - } - return Middleware::Type::UNKNOWN; -} +// static const std::unordered_map MIDDLEWARE_NAME_TYPE_MAP = { +// - {"", Middleware::Type::DAPR}, // default +// - {"dapr", Middleware::Type::DAPR}, +// - {"native", Middleware::Type::NATIVE}, +// -}; +// - + +std::string Middleware::getTypeDefiningEnvVarName() { return "SDV_MIDDLEWARE_TYPE"; } std::unique_ptr Middleware::instantiate() { - std::string middlewareName = getEnvVar(ENV_MIDDLEWARE_TYPE); - Middleware::Type middlewareType = getMiddlewareType(middlewareName); - switch (middlewareType) { - case Type::NATIVE: + const std::string middlewareType = StringUtils::toLower(getEnvVar(getTypeDefiningEnvVarName())); + if (middlewareType.empty()) { + return std::make_unique(); + } else if (middlewareType == NativeMiddleware::TYPE_ID) { return std::make_unique(); - case Type::DAPR: + } else if (middlewareType == DaprMiddleware::TYPE_ID) { return std::make_unique(); - default: - throw std::runtime_error(fmt::format("Unknown middleware type '{}' -> {}", middlewareName, - static_cast(middlewareType))); + } else { + throw std::runtime_error(fmt::format("Unknown middleware type '{}'", middlewareType)); } } diff --git a/sdk/src/sdk/middleware/NativeMiddleware.cpp b/sdk/src/sdk/middleware/NativeMiddleware.cpp index ea691f7c..f4da80da 100644 --- a/sdk/src/sdk/middleware/NativeMiddleware.cpp +++ b/sdk/src/sdk/middleware/NativeMiddleware.cpp @@ -16,6 +16,7 @@ #include "NativeMiddleware.h" +#include "sdk/IPubSubClient.h" #include "sdk/Logger.h" #include "sdk/Utils.h" #include "sdk/dapr/DaprSupport.h" @@ -40,7 +41,7 @@ static std::string getDefaultLocation(const std::string& serviceName) { return defaultLocation; }; -std::string NativeMiddleware::getServiceEnvVarName(const std::string& serviceName) const { +static std::string getServiceEnvVarName(const std::string& serviceName) { return "SDV_" + StringUtils::toUpper(serviceName) + "_ADDRESS"; } @@ -53,4 +54,10 @@ std::string NativeMiddleware::getServiceLocation(const std::string& serviceName) return serviceAddress; } +std::shared_ptr +NativeMiddleware::createPubSubClient(const std::string& clientId) const { + std::string brokerLocation = getServiceLocation("mqtt"); + return IPubSubClient::createInstance(brokerLocation, clientId); +} + } // namespace velocitas diff --git a/sdk/src/sdk/middleware/NativeMiddleware.h b/sdk/src/sdk/middleware/NativeMiddleware.h index f68e7b72..ce6770d3 100644 --- a/sdk/src/sdk/middleware/NativeMiddleware.h +++ b/sdk/src/sdk/middleware/NativeMiddleware.h @@ -23,13 +23,13 @@ namespace velocitas { class NativeMiddleware : public Middleware { public: - NativeMiddleware() = default; + static constexpr char const* TYPE_ID = "native"; - Type getType() const override { return Type::NATIVE; } - - std::string getServiceEnvVarName(const std::string& serviceName) const; + NativeMiddleware() + : Middleware(TYPE_ID) {} std::string getServiceLocation(const std::string& serviceName) const override; + std::shared_ptr createPubSubClient(const std::string& clientId) const override; }; } // namespace velocitas diff --git a/sdk/src/sdk/pubsub/MqttPubSubClient.cpp b/sdk/src/sdk/pubsub/MqttPubSubClient.cpp index c54b8792..c52cb724 100644 --- a/sdk/src/sdk/pubsub/MqttPubSubClient.cpp +++ b/sdk/src/sdk/pubsub/MqttPubSubClient.cpp @@ -20,7 +20,7 @@ #include "sdk/Status.h" #include "sdk/ThreadPool.h" -#include "sdk/middleware/NativeMiddleware.h" +#include "sdk/middleware/Middleware.h" #include #include @@ -46,10 +46,8 @@ class MqttPubSubClient : public IPubSubClient, private mqtt::callback { auto* const brokerUri = getenv("MQTT_BROKER_URI"); if (brokerUri != nullptr) { - logger().warn("You're use deprecated environment variable MQTT_BROKER_URI. Support for " - "this will be removed soon.\n" - "Please switch to current variable {}.", - NativeMiddleware().getServiceEnvVarName("mqtt")); + logger().warn("You're using the deprecated environment variable MQTT_BROKER_URI. " + "Support for this will be removed soon."); optionsBuilder.servers( std::make_shared(std::string{brokerUri})); } @@ -101,13 +99,7 @@ class MqttPubSubClient : public IPubSubClient, private mqtt::callback { }; std::shared_ptr IPubSubClient::createInstance(const std::string& clientId) { - if (Config::getMiddleware().getType() == Middleware::Type::DAPR) { - logger().warn("Velocitas' C++ SDK does not yet support Dapr PubSub " - "-> connecting directly to MQTT broker!"); - } - - std::string brokerLocation = NativeMiddleware().getServiceLocation("mqtt"); - return std::make_shared(brokerLocation, clientId); + return Config::getMiddleware().createPubSubClient(clientId); } std::shared_ptr IPubSubClient::createInstance(const std::string& brokerUri, diff --git a/sdk/tests/unit/CMakeLists.txt b/sdk/tests/unit/CMakeLists.txt index c5e384e3..66959233 100644 --- a/sdk/tests/unit/CMakeLists.txt +++ b/sdk/tests/unit/CMakeLists.txt @@ -1,12 +1,16 @@ set(TARGET_NAME "sdk_utests") add_executable(${TARGET_NAME} + testmain.cpp AsyncResult_tests.cpp AsyncSubscription_tests.cpp DataPoint_tests.cpp DataPointBatch_tests.cpp Job_tests.cpp Logger_tests.cpp + Middleware_tests.cpp + DaprMiddleware_tests.cpp + NativeMiddleware_tests.cpp Node_tests.cpp ScopedBoolInverter_tests.cpp ThreadPool_tests.cpp @@ -22,13 +26,15 @@ add_dependencies(${TARGET_NAME} target_link_libraries(${TARGET_NAME} vehicle-app-sdk - gtest_main gmock ) include(GoogleTest) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../mocks) +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/../mocks + ${CMAKE_CURRENT_SOURCE_DIR}/../../src +) if(NOT CMAKE_TOOLCHAIN_FILE) gtest_discover_tests(${TARGET_NAME}) diff --git a/sdk/tests/unit/DaprMiddleware_tests.cpp b/sdk/tests/unit/DaprMiddleware_tests.cpp new file mode 100644 index 00000000..22bc5472 --- /dev/null +++ b/sdk/tests/unit/DaprMiddleware_tests.cpp @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2023 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdk/middleware/DaprMiddleware.h" + +#include + +#include + +using namespace velocitas; + +class Test_DaprMiddleware : public ::testing::Test { +protected: + Middleware& getCut() { return m_cut; } + +private: + DaprMiddleware m_cut; +}; + +TEST_F(Test_DaprMiddleware, getTypeId__typeIdIsDapr) { + ::std::string typeId = getCut().getTypeId(); + EXPECT_EQ(DaprMiddleware::TYPE_ID, typeId); +} + +// paho_mqtt_cpp throws +// mqtt::exception with description "MQTT error [-14]: Invalid protocol scheme". +TEST_F(Test_DaprMiddleware, DISABLED_createPubSubClient__validPointer) { + std::shared_ptr pubSubClient; + EXPECT_NO_THROW(pubSubClient = getCut().createPubSubClient("My Test Id")); + EXPECT_NE(nullptr, pubSubClient.get()); +} + +TEST_F(Test_DaprMiddleware, getServiceLocation_envDaprGrpcPortSet_contentOfEnvVar) { + ::setenv("DAPR_GRPC_PORT", "12345", /*overwrite=*/true); + auto serviceLocation = getCut().getServiceLocation("SomeService"); + EXPECT_EQ("grpc://localhost:12345", serviceLocation); +} + +TEST_F(Test_DaprMiddleware, getServiceLocation_envDaprGrpcPortNotSet_throwsRuntimeError) { + ::unsetenv("DAPR_GRPC_PORT"); + EXPECT_THROW(getCut().getServiceLocation("UnknownService"), std::runtime_error); +} + +TEST_F(Test_DaprMiddleware, getMetadata_envVarWithApppIdSet_metadataWithContentOfEnvVar) { + ::setenv("SOMESERVICE_DAPR_APP_ID", "some-explicit-app-id", /*overwrite=*/true); + Middleware::Metadata metadata = getCut().getMetadata("SomeService"); + Middleware::Metadata expectedMetadata = {{"dapr-app-id", "some-explicit-app-id"}}; + EXPECT_EQ(expectedMetadata, metadata); +} + +TEST_F(Test_DaprMiddleware, getMetadata_envVarWithApppIdNotSet_metadataWithDefaultAppId) { + ::unsetenv("SOMESERVICE_DAPR_APP_ID"); + Middleware::Metadata metadata = getCut().getMetadata("SomeService"); + Middleware::Metadata expectedMetadata = {{"dapr-app-id", "someservice"}}; + EXPECT_EQ(expectedMetadata, metadata); +} diff --git a/sdk/tests/unit/Middleware_tests.cpp b/sdk/tests/unit/Middleware_tests.cpp new file mode 100644 index 00000000..eae8411f --- /dev/null +++ b/sdk/tests/unit/Middleware_tests.cpp @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2022 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include + +#define private public +#include "sdk/middleware/DaprMiddleware.h" +#include "sdk/middleware/Middleware.h" +#include "sdk/middleware/NativeMiddleware.h" + +using namespace velocitas; + +TEST(Test_Middleware, getTypeDefiningEnvVarName__noneEmptyString) { + EXPECT_FALSE(Middleware::getTypeDefiningEnvVarName().empty()); +} + +TEST(Test_Middleware, getInstantance_envVarGloballySetToNative_TypeIdIsNative) { + Middleware& middleware = Middleware::getInstance(); + EXPECT_EQ(NativeMiddleware::TYPE_ID, middleware.getTypeId()); +} + +TEST(Test_Middleware, instantiate_envVarNotSet_typeIdIsDapr) { + ::unsetenv(Middleware::getTypeDefiningEnvVarName().c_str()); + auto middleware = Middleware::instantiate(); + EXPECT_EQ(DaprMiddleware::TYPE_ID, middleware->getTypeId()); +} + +TEST(Test_Middleware, instantiate_envVarSetToDapr_typeIdIsDapr) { + ::setenv(Middleware::getTypeDefiningEnvVarName().c_str(), DaprMiddleware::TYPE_ID, + /*overwrite=*/true); + EXPECT_EQ(DaprMiddleware::TYPE_ID, Middleware::instantiate()->getTypeId()); +} + +TEST(Test_Middleware, instantiate_envVarSetToNative_TypeIdIsNative) { + ::setenv(Middleware::getTypeDefiningEnvVarName().c_str(), NativeMiddleware::TYPE_ID, + /*overwrite=*/true); + EXPECT_EQ(NativeMiddleware::TYPE_ID, Middleware::instantiate()->getTypeId()); +} + +TEST(Test_Middleware, instantiate_envVarSetToUndefined_throwRuntimeError) { + ::setenv(Middleware::getTypeDefiningEnvVarName().c_str(), "something undefined", + /*overwrite=*/true); + EXPECT_THROW(Middleware::instantiate(), std::runtime_error); +} diff --git a/sdk/tests/unit/NativeMiddleware_tests.cpp b/sdk/tests/unit/NativeMiddleware_tests.cpp new file mode 100644 index 00000000..f9cc6de6 --- /dev/null +++ b/sdk/tests/unit/NativeMiddleware_tests.cpp @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2023 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdk/middleware/NativeMiddleware.h" + +#include + +#include + +using namespace velocitas; + +class Test_NativeMiddleware : public ::testing::Test { +protected: + Middleware& getCut() { return m_cut; } + +private: + NativeMiddleware m_cut; +}; + +TEST_F(Test_NativeMiddleware, getTypeId__typeIdIsNative) { + ::std::string typeId = getCut().getTypeId(); + EXPECT_EQ(NativeMiddleware::TYPE_ID, typeId); +} + +// paho_mqtt_cpp throws +// mqtt::exception with description "MQTT error [-14]: Invalid protocol scheme". +TEST_F(Test_NativeMiddleware, DISABLED_createPubSubClient__validPointer) { + std::shared_ptr pubSubClient; + EXPECT_NO_THROW(pubSubClient = getCut().createPubSubClient("My Test Id")); + EXPECT_NE(nullptr, pubSubClient.get()); +} + +TEST_F(Test_NativeMiddleware, getServiceLocation_envVarNotSet_emptyString) { + auto serviceLocation = getCut().getServiceLocation("UnknownService"); + EXPECT_EQ("", serviceLocation); +} + +TEST_F(Test_NativeMiddleware, getServiceLocation_envVarSet_contentOfEnvVar) { + ::setenv("SDV_SOMESERVICE_ADDRESS", "some-service-address", /*overwrite=*/true); + auto serviceLocation = getCut().getServiceLocation("SomeService"); + EXPECT_EQ("some-service-address", serviceLocation); +} + +TEST_F(Test_NativeMiddleware, getMetadata__emptyMap) { + Middleware::Metadata metadata = getCut().getMetadata("don't care"); + EXPECT_TRUE(metadata.empty()); +} diff --git a/sdk/tests/unit/Utils_tests.cpp b/sdk/tests/unit/Utils_tests.cpp index fa9d999a..79146dea 100644 --- a/sdk/tests/unit/Utils_tests.cpp +++ b/sdk/tests/unit/Utils_tests.cpp @@ -20,6 +20,48 @@ using namespace velocitas; +TEST(Utils, getEnvVar_varNotSet_defaultValue) { + ::unsetenv("MY_TEST_ENV_VAR"); + std::string varContent = getEnvVar("MY_TEST_ENV_VAR", "default content"); + EXPECT_EQ("default content", varContent); +} + +TEST(Utils, getEnvVar_emptyVar_emptyString) { + ::setenv("MY_TEST_ENV_VAR", "", /*overwrite=*/true); + std::string varContent = getEnvVar("MY_TEST_ENV_VAR", "default content"); + EXPECT_EQ("", varContent); +} + +TEST(Utils, getEnvVar_nonEmptyVar_varContent) { + ::setenv("MY_TEST_ENV_VAR", "some content", /*overwrite=*/true); + std::string varContent = getEnvVar("MY_TEST_ENV_VAR", "default content"); + EXPECT_EQ("some content", varContent); +} + +TEST(StringUtils, toLower_emptyString_emptyString) { + EXPECT_TRUE(StringUtils::toLower("").empty()); +} + +TEST(StringUtils, toUpper_emptyString_emptyString) { + EXPECT_TRUE(StringUtils::toUpper("").empty()); +} + +static const std::string + MixedAscii(" !\"#$%&'()*+,-./" + "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"); + +TEST(StringUtils, toLower_asciiString_allUpperAreLower) { + EXPECT_EQ(" !\"#$%&'()*+,-./" + "0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", + StringUtils::toLower(MixedAscii)); +} + +TEST(StringUtils, toUpper_asciiString_allLowerAreUpper) { + EXPECT_EQ(" !\"#$%&'()*+,-./" + "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~", + StringUtils::toUpper(MixedAscii)); +} + TEST(StringUtils, join_emptyVector_emptyString) { std::vector v; auto result = StringUtils::join(v, ","); diff --git a/sdk/tests/unit/testmain.cpp b/sdk/tests/unit/testmain.cpp new file mode 100644 index 00000000..25ea676d --- /dev/null +++ b/sdk/tests/unit/testmain.cpp @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2023 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdk/middleware/Middleware.h" +#include "sdk/middleware/NativeMiddleware.h" + +#include +#include + +using namespace velocitas; + +class SetupMiddlewareSingleton : public ::testing::Environment { +public: + void SetUp() override { + ::setenv(Middleware::getTypeDefiningEnvVarName().c_str(), NativeMiddleware::TYPE_ID, + /*overwrite=*/true); + std::ignore = Middleware::getInstance(); + } +}; + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + std::ignore = testing::AddGlobalTestEnvironment(new SetupMiddlewareSingleton); + return RUN_ALL_TESTS(); +} From 5af8bd0300f7bbc9b19eb6c9e7999701ce8029c6 Mon Sep 17 00:00:00 2001 From: BjoernAtBosch Date: Wed, 28 Jun 2023 17:55:04 +0200 Subject: [PATCH 05/16] Fixes and improvements --- .../Cabin/SeatService/SeatService.cpp | 14 +++++---- sdk/include/sdk/Model.h | 6 +++- sdk/include/sdk/middleware/Middleware.h | 2 +- sdk/src/CMakeLists.txt | 1 + sdk/src/sdk/Model.cpp | 30 +++++++++++++++++++ sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp | 1 + sdk/src/sdk/middleware/DaprMiddleware.cpp | 4 +-- sdk/src/sdk/middleware/Middleware.cpp | 7 ----- sdk/src/sdk/middleware/NativeMiddleware.cpp | 11 ++++++- sdk/src/sdk/pubsub/MqttPubSubClient.cpp | 5 ++++ sdk/tests/unit/DaprMiddleware_tests.cpp | 4 +-- sdk/tests/unit/Middleware_tests.cpp | 2 +- sdk/tests/unit/NativeMiddleware_tests.cpp | 4 +-- 13 files changed, 67 insertions(+), 24 deletions(-) create mode 100644 sdk/src/sdk/Model.cpp diff --git a/examples/vehicle_model/Cabin/SeatService/SeatService.cpp b/examples/vehicle_model/Cabin/SeatService/SeatService.cpp index 1f484dd0..a585347d 100644 --- a/examples/vehicle_model/Cabin/SeatService/SeatService.cpp +++ b/examples/vehicle_model/Cabin/SeatService/SeatService.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2022 Robert Bosch GmbH + * Copyright (c) 2022-2023 Robert Bosch GmbH * * This program and the accompanying materials are made available under the * terms of the Apache License, Version 2.0 which is available at @@ -81,11 +81,15 @@ Status toInternalStatus(grpc::Status status) { } SeatService::SeatService(Model* parent) - : Service("VehicleService", parent) { + : Service("SeatService", parent) { m_asyncGrpcFacade = std::make_shared( - grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials())); - m_asyncGrpcFacade->setContextModifier( - [this](auto& context) { context.AddMetadata("dapr-app-id", getName()); }); + grpc::CreateChannel(getLocation(), grpc::InsecureChannelCredentials())); + Middleware::Metadata metadata = getMiddlewareMetadata(); + m_asyncGrpcFacade->setContextModifier([metadata](auto& context) { + for (auto metadatum : metadata) { + context.AddMetadata(metadatum.first, metadatum.second); + } + }); } AsyncResultPtr_t SeatService::move(const SeatService::Seat& seat) { diff --git a/sdk/include/sdk/Model.h b/sdk/include/sdk/Model.h index c86e19f9..86949686 100644 --- a/sdk/include/sdk/Model.h +++ b/sdk/include/sdk/Model.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2022 Robert Bosch GmbH + * Copyright (c) 2022-2023 Robert Bosch GmbH * * This program and the accompanying materials are made available under the * terms of the Apache License, Version 2.0 which is available at @@ -19,6 +19,7 @@ #include "sdk/DataPointBatch.h" #include "sdk/Node.h" +#include "sdk/middleware/Middleware.h" #include @@ -43,6 +44,9 @@ class Model : public Node { class Service : public Node { public: using Node::Node; + + std::string getLocation() const; + Middleware::Metadata getMiddlewareMetadata() const; }; } // namespace velocitas diff --git a/sdk/include/sdk/middleware/Middleware.h b/sdk/include/sdk/middleware/Middleware.h index 7b531c58..90fb14dd 100644 --- a/sdk/include/sdk/middleware/Middleware.h +++ b/sdk/include/sdk/middleware/Middleware.h @@ -102,7 +102,7 @@ class Middleware { * @param typeId unique identifier for the type of the concrete middleware * implementation */ - Middleware(std::string&& typeId) + explicit Middleware(std::string&& typeId) : m_typeId(std::move(typeId)) {} private: diff --git a/sdk/src/CMakeLists.txt b/sdk/src/CMakeLists.txt index 53f265cc..ada2e17e 100644 --- a/sdk/src/CMakeLists.txt +++ b/sdk/src/CMakeLists.txt @@ -16,6 +16,7 @@ set(TARGET_NAME "vehicle-app-sdk") add_library(${TARGET_NAME} sdk/VehicleApp.cpp + sdk/Model.cpp sdk/Node.cpp sdk/QueryBuilder.cpp sdk/DataPoint.cpp diff --git a/sdk/src/sdk/Model.cpp b/sdk/src/sdk/Model.cpp new file mode 100644 index 00000000..be5c7099 --- /dev/null +++ b/sdk/src/sdk/Model.cpp @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2023 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdk/Model.h" +#include "sdk/Config.h" + +namespace velocitas { + +std::string Service::getLocation() const { + return Config::getMiddleware().getServiceLocation(getName()); +} + +Middleware::Metadata Service::getMiddlewareMetadata() const { + return Config::getMiddleware().getMetadata(getName()); +} + +} // namespace velocitas diff --git a/sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp b/sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp index 50372df4..b897777a 100644 --- a/sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp +++ b/sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp @@ -38,6 +38,7 @@ namespace velocitas { VehicleDataBrokerClient::VehicleDataBrokerClient(const std::string& vdbAddress, std::string vdbServiceName) { + logger().info("Connecting to data broker service '{}' via {}", vdbServiceName, vdbAddress); m_asyncBrokerFacade = std::make_shared( grpc::CreateChannel(vdbAddress, grpc::InsecureChannelCredentials())); Middleware::Metadata metadata = Config::getMiddleware().getMetadata(vdbServiceName); diff --git a/sdk/src/sdk/middleware/DaprMiddleware.cpp b/sdk/src/sdk/middleware/DaprMiddleware.cpp index 23801556..f043d3ab 100644 --- a/sdk/src/sdk/middleware/DaprMiddleware.cpp +++ b/sdk/src/sdk/middleware/DaprMiddleware.cpp @@ -41,7 +41,7 @@ namespace dapr { void waitForSidecar() { auto sidecarHttpPort = getEnvVar(ENV_DAPR_HTTP_PORT); if (sidecarHttpPort.empty()) { - logger().info("dapr: env {} not set. Continuing without sidecar health check ...", + logger().warn("dapr: env {} not set. Continuing without sidecar health check ...", ENV_DAPR_HTTP_PORT); } else { logger().info("dapr: env {} set. Waiting for sidecar at port {} ...", ENV_DAPR_HTTP_PORT, @@ -57,7 +57,7 @@ void waitForSidecar() { logger().debug("dapr: Health endpoint returned status code: {}, {}", response.status_code, response.text); } catch (const std::exception& e) { - logger().debug("dapr: Exception occurred during health endpoint: {}", e.what()); + logger().warn("dapr: Exception occurred requesting health endpoint: {}", e.what()); } std::this_thread::sleep_for(std::chrono::seconds(1)); } diff --git a/sdk/src/sdk/middleware/Middleware.cpp b/sdk/src/sdk/middleware/Middleware.cpp index 95bdab30..5f697a8c 100644 --- a/sdk/src/sdk/middleware/Middleware.cpp +++ b/sdk/src/sdk/middleware/Middleware.cpp @@ -29,13 +29,6 @@ namespace velocitas { -// static const std::unordered_map MIDDLEWARE_NAME_TYPE_MAP = { -// - {"", Middleware::Type::DAPR}, // default -// - {"dapr", Middleware::Type::DAPR}, -// - {"native", Middleware::Type::NATIVE}, -// -}; -// - - std::string Middleware::getTypeDefiningEnvVarName() { return "SDV_MIDDLEWARE_TYPE"; } std::unique_ptr Middleware::instantiate() { diff --git a/sdk/src/sdk/middleware/NativeMiddleware.cpp b/sdk/src/sdk/middleware/NativeMiddleware.cpp index f4da80da..0d835495 100644 --- a/sdk/src/sdk/middleware/NativeMiddleware.cpp +++ b/sdk/src/sdk/middleware/NativeMiddleware.cpp @@ -28,7 +28,7 @@ namespace velocitas { static const std::unordered_map DEFAULT_LOCATIONS = { - {"mqtt", "mqtt://localhost:1883"}, + {"mqtt", "tcp://localhost:1883"}, {"vehicledatabroker", "grpc://localhost:55555"}, }; @@ -50,6 +50,15 @@ std::string NativeMiddleware::getServiceLocation(const std::string& serviceName) auto serviceAddress = getEnvVar(envVarName); if (serviceAddress.empty()) { serviceAddress = getDefaultLocation(serviceName); + if (serviceAddress.empty()) { + logger().error( + "Env variable '{}' defining location of service '{}' not set. Please define!", + envVarName, serviceName); + } else { + logger().warn( + "Env variable '{}' defining location of service '{}' not set. Taking default: '{}'", + envVarName, serviceName, serviceAddress); + } } return serviceAddress; } diff --git a/sdk/src/sdk/pubsub/MqttPubSubClient.cpp b/sdk/src/sdk/pubsub/MqttPubSubClient.cpp index c52cb724..00127d73 100644 --- a/sdk/src/sdk/pubsub/MqttPubSubClient.cpp +++ b/sdk/src/sdk/pubsub/MqttPubSubClient.cpp @@ -50,6 +50,11 @@ class MqttPubSubClient : public IPubSubClient, private mqtt::callback { "Support for this will be removed soon."); optionsBuilder.servers( std::make_shared(std::string{brokerUri})); + logger().info("Connecting to MQTT broker at '{}' with client-id '{}'", brokerUri, + m_client.get_client_id()); + } else { + logger().info("Connecting to MQTT broker at '{}' with client-id '{}'", + m_client.get_server_uri(), m_client.get_client_id()); } m_client.connect(optionsBuilder.finalize())->wait(); diff --git a/sdk/tests/unit/DaprMiddleware_tests.cpp b/sdk/tests/unit/DaprMiddleware_tests.cpp index 22bc5472..2826d0f4 100644 --- a/sdk/tests/unit/DaprMiddleware_tests.cpp +++ b/sdk/tests/unit/DaprMiddleware_tests.cpp @@ -35,9 +35,7 @@ TEST_F(Test_DaprMiddleware, getTypeId__typeIdIsDapr) { EXPECT_EQ(DaprMiddleware::TYPE_ID, typeId); } -// paho_mqtt_cpp throws -// mqtt::exception with description "MQTT error [-14]: Invalid protocol scheme". -TEST_F(Test_DaprMiddleware, DISABLED_createPubSubClient__validPointer) { +TEST_F(Test_DaprMiddleware, createPubSubClient__validPointer) { std::shared_ptr pubSubClient; EXPECT_NO_THROW(pubSubClient = getCut().createPubSubClient("My Test Id")); EXPECT_NE(nullptr, pubSubClient.get()); diff --git a/sdk/tests/unit/Middleware_tests.cpp b/sdk/tests/unit/Middleware_tests.cpp index eae8411f..281a80a4 100644 --- a/sdk/tests/unit/Middleware_tests.cpp +++ b/sdk/tests/unit/Middleware_tests.cpp @@ -30,7 +30,7 @@ TEST(Test_Middleware, getTypeDefiningEnvVarName__noneEmptyString) { } TEST(Test_Middleware, getInstantance_envVarGloballySetToNative_TypeIdIsNative) { - Middleware& middleware = Middleware::getInstance(); + const Middleware& middleware = Middleware::getInstance(); EXPECT_EQ(NativeMiddleware::TYPE_ID, middleware.getTypeId()); } diff --git a/sdk/tests/unit/NativeMiddleware_tests.cpp b/sdk/tests/unit/NativeMiddleware_tests.cpp index f9cc6de6..e065ae64 100644 --- a/sdk/tests/unit/NativeMiddleware_tests.cpp +++ b/sdk/tests/unit/NativeMiddleware_tests.cpp @@ -35,9 +35,7 @@ TEST_F(Test_NativeMiddleware, getTypeId__typeIdIsNative) { EXPECT_EQ(NativeMiddleware::TYPE_ID, typeId); } -// paho_mqtt_cpp throws -// mqtt::exception with description "MQTT error [-14]: Invalid protocol scheme". -TEST_F(Test_NativeMiddleware, DISABLED_createPubSubClient__validPointer) { +TEST_F(Test_NativeMiddleware, createPubSubClient__validPointer) { std::shared_ptr pubSubClient; EXPECT_NO_THROW(pubSubClient = getCut().createPubSubClient("My Test Id")); EXPECT_NE(nullptr, pubSubClient.get()); From 56e7ab1b3c0798fae04913884ee7c89d1ee20f36 Mon Sep 17 00:00:00 2001 From: BjoernAtBosch Date: Thu, 29 Jun 2023 12:27:12 +0200 Subject: [PATCH 06/16] Fix addresses and setDatapoints example --- .../set-data-points/src/SetDataPointsApp.cpp | 41 ++++++++++++++----- sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp | 2 +- sdk/src/sdk/middleware/DaprMiddleware.cpp | 2 +- sdk/src/sdk/middleware/NativeMiddleware.cpp | 4 +- sdk/tests/unit/DaprMiddleware_tests.cpp | 2 +- 5 files changed, 36 insertions(+), 15 deletions(-) diff --git a/examples/set-data-points/src/SetDataPointsApp.cpp b/examples/set-data-points/src/SetDataPointsApp.cpp index e8c90713..facd558c 100644 --- a/examples/set-data-points/src/SetDataPointsApp.cpp +++ b/examples/set-data-points/src/SetDataPointsApp.cpp @@ -34,20 +34,41 @@ class SetDataPointsApp : public velocitas::VehicleApp { velocitas::IPubSubClient::createInstance("SetDataPointsApp")) {} void onStart() override { - velocitas::logger().info("Setting data points!"); + // set a single data point + try { + velocitas::logger().info("Setting single data point ..."); + + Vehicle.Speed.set(100.0F)->await(); + + velocitas::logger().info("Setting single data point successfully done."); + } catch (velocitas::AsyncException& e) { + velocitas::logger().error("Error on setting single data point: {}", e.what()); + } // set multiple data points at the same time - Vehicle.setMany() - .add(Vehicle.Cabin.Seat.Row1.Pos1.Position, 1000) - .add(Vehicle.Cabin.Seat.Row1.Pos2.Position, 1000) - .apply() - ->await(); + try { + velocitas::logger().info("Setting batch of data points ..."); - // set a single data point - Vehicle.Speed.set(100.0F)->await(); + auto result = Vehicle.setMany() + .add(Vehicle.Cabin.Seat.Row1.Pos1.Position, 1000) + .add(Vehicle.Cabin.Seat.Row1.Pos2.Position, 1000) + .apply() + ->await(); + + if (result.empty()) { + velocitas::logger().info("Setting batch of data points successfully done."); + } else { + velocitas::logger().error("Some data points of batch could not be set:"); + for (auto datapointError : result) { + velocitas::logger().error(" '{}' -> {}", datapointError.first, + datapointError.second); + } + } + } catch (velocitas::AsyncException& e) { + velocitas::logger().error("Error on setting batch of data points: {}", e.what()); + } - // get a single data point - Vehicle.Cabin.Seat.Row1.Pos1.Position.get()->await(); + velocitas::logger().info("Done. (Press Ctrl+C to terminate the app.)"); } private: diff --git a/sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp b/sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp index b897777a..103bddc6 100644 --- a/sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp +++ b/sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp @@ -38,7 +38,7 @@ namespace velocitas { VehicleDataBrokerClient::VehicleDataBrokerClient(const std::string& vdbAddress, std::string vdbServiceName) { - logger().info("Connecting to data broker service '{}' via {}", vdbServiceName, vdbAddress); + logger().info("Connecting to data broker service '{}' via '{}'", vdbServiceName, vdbAddress); m_asyncBrokerFacade = std::make_shared( grpc::CreateChannel(vdbAddress, grpc::InsecureChannelCredentials())); Middleware::Metadata metadata = Config::getMiddleware().getMetadata(vdbServiceName); diff --git a/sdk/src/sdk/middleware/DaprMiddleware.cpp b/sdk/src/sdk/middleware/DaprMiddleware.cpp index f043d3ab..64679f9f 100644 --- a/sdk/src/sdk/middleware/DaprMiddleware.cpp +++ b/sdk/src/sdk/middleware/DaprMiddleware.cpp @@ -76,7 +76,7 @@ std::string DaprMiddleware::getServiceLocation(const std::string& serviceName) c throw std::runtime_error( fmt::format("{} not set! Cannot connect to Dapr sidecar!", ENV_DAPR_GRPC_PORT)); } - return "grpc://localhost:" + daprSidecarGrpcPort; + return "localhost:" + daprSidecarGrpcPort; }; Middleware::Metadata DaprMiddleware::getMetadata(const std::string& serviceName) const { diff --git a/sdk/src/sdk/middleware/NativeMiddleware.cpp b/sdk/src/sdk/middleware/NativeMiddleware.cpp index 0d835495..7c1a292f 100644 --- a/sdk/src/sdk/middleware/NativeMiddleware.cpp +++ b/sdk/src/sdk/middleware/NativeMiddleware.cpp @@ -28,8 +28,8 @@ namespace velocitas { static const std::unordered_map DEFAULT_LOCATIONS = { - {"mqtt", "tcp://localhost:1883"}, - {"vehicledatabroker", "grpc://localhost:55555"}, + {"mqtt", "localhost:1883"}, + {"vehicledatabroker", "localhost:55555"}, }; static std::string getDefaultLocation(const std::string& serviceName) { diff --git a/sdk/tests/unit/DaprMiddleware_tests.cpp b/sdk/tests/unit/DaprMiddleware_tests.cpp index 2826d0f4..9e8070ab 100644 --- a/sdk/tests/unit/DaprMiddleware_tests.cpp +++ b/sdk/tests/unit/DaprMiddleware_tests.cpp @@ -44,7 +44,7 @@ TEST_F(Test_DaprMiddleware, createPubSubClient__validPointer) { TEST_F(Test_DaprMiddleware, getServiceLocation_envDaprGrpcPortSet_contentOfEnvVar) { ::setenv("DAPR_GRPC_PORT", "12345", /*overwrite=*/true); auto serviceLocation = getCut().getServiceLocation("SomeService"); - EXPECT_EQ("grpc://localhost:12345", serviceLocation); + EXPECT_EQ("localhost:12345", serviceLocation); } TEST_F(Test_DaprMiddleware, getServiceLocation_envDaprGrpcPortNotSet_throwsRuntimeError) { From c027f74c7673748941968887d6a3a0ecab05c0ad Mon Sep 17 00:00:00 2001 From: BjoernAtBosch Date: Fri, 30 Jun 2023 10:24:12 +0200 Subject: [PATCH 07/16] Update README --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 33458b51..917394d9 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ With the runtime running in the background, you can run the app. Open the `Run Task` view in VSCode and select `Local - VehicleApp (Dapr run)`. ### With debugging -You can simply launch the example in the Debugging Tab. Make sure the `VehicleApp - Debug (dapr run)` is selected at the top. After the selection is done, you can also simply hit `F5`, to start the debugging session. +You can simply launch the example in the Debugging Tab. Make sure the `Example - ` is selected at the top. After the selection is done, you can also simply hit `F5`, to start the debugging session. *Note: This launch task will also make sure to re-build the app if it has been modified!* @@ -69,6 +69,24 @@ docker run --net="host" --mount type=bind,source="$(pwd)"/.dapr,target=/.dapr da docker run --rm -it --net="host" -e DAPR_GRPC_PORT=50001 -e DAPR_HTTP_PORT=3500 localhost:12345/vehicleapp:local ``` +### Middleware configuration for the example apps + +You can configure the middleware to be used (i.e. either `dapr` or `native`) via environment variables of the app processs. + +| Middleware | Environment Variable | Default | Meaning +|------------|---------------------------------|---------------------|------------------------------------ +| | `SDV_MIDDLEWARE_TYPE` | `dapr` | Defines the middleware to be used by the app (either `dapr` or `native`) +| +| Dapr | `SDV_MQTT_ADDRESS` | `localhost:1883` | Address of the MQTT broker - needs to be set explicitly as the C++ SDK does not support Dapr PubSub, yet +| | `SEATSERVICE_DAPR_APP_ID` | `seatservice` | Application id used by Dapr to identifying the application providing the seat service +| | `VEHICLEDATABROKER_DAPR_APP_ID` | `vehicledatabroker` | Application id used by Dapr to identifying the application providing the seat service +| | (`DAPR_GRPC_PORT`) | - | Usually, `DAPR_GRPC_PORT` and `DAPR_HTTP_PORT` don't need to be configured manually. They are set by `dapr run` when starting the app and its sidecar. +| | (`DAPR_HTTP_PORT`) | - | If you need to start your app separately, please make sure these ports are set equally to the app and to its sidecar (see above). +| | +| native | `SDV_MQTT_ADDRESS` | `localhost:1883` | Address of the MQTT broker +| | `SDV_SEATSERVICE_ADDRESS` | - | Address of the seat service +| | `SDV_VEHICLEDATABROKER_ADDRESS` | `localhost:55555` | Address of the Kuksa (Vehicle) Data Broker + ## Documentation * [Velocitas Development Model](https://eclipse.dev/velocitas/docs/concepts/development_model/) * [Vehicle App SDK Overview](https://eclipse.dev/velocitas/docs/concepts/development_model/vehicle_app_sdk/) From 14c2415e5ea9fb03636e99e895d52d2da8e579d4 Mon Sep 17 00:00:00 2001 From: BjoernAtBosch Date: Fri, 30 Jun 2023 10:41:24 +0200 Subject: [PATCH 08/16] Increase SDK version --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index dadd90d5..1ae3db0b 100644 --- a/conanfile.py +++ b/conanfile.py @@ -19,7 +19,7 @@ class VehicleAppCppSdkConan(ConanFile): name = "vehicle-app-sdk" - version = "0.3.2" + version = "0.3.3" license = "Apache-2.0" url = "https://github.com/eclipse-velocitas/vehicle-app-cpp-sdk" description = "The Vehicle App SDK for c++ allows to create Vehicle Apps from the Velocitas development model in the c++ programming language." From 67cf0d5da47ede64c216e900590dca54d984fb44 Mon Sep 17 00:00:00 2001 From: BjoernAtBosch Date: Mon, 3 Jul 2023 16:39:56 +0200 Subject: [PATCH 09/16] Give warning if deprecated 'MQTT_BROKER_URI' is defined --- sdk/src/sdk/pubsub/MqttPubSubClient.cpp | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/sdk/src/sdk/pubsub/MqttPubSubClient.cpp b/sdk/src/sdk/pubsub/MqttPubSubClient.cpp index 00127d73..2e2547ef 100644 --- a/sdk/src/sdk/pubsub/MqttPubSubClient.cpp +++ b/sdk/src/sdk/pubsub/MqttPubSubClient.cpp @@ -42,22 +42,16 @@ class MqttPubSubClient : public IPubSubClient, private mqtt::callback { ~MqttPubSubClient() override = default; void connect() override { - auto optionsBuilder = mqtt::connect_options_builder(); - - auto* const brokerUri = getenv("MQTT_BROKER_URI"); - if (brokerUri != nullptr) { - logger().warn("You're using the deprecated environment variable MQTT_BROKER_URI. " - "Support for this will be removed soon."); - optionsBuilder.servers( - std::make_shared(std::string{brokerUri})); - logger().info("Connecting to MQTT broker at '{}' with client-id '{}'", brokerUri, - m_client.get_client_id()); - } else { - logger().info("Connecting to MQTT broker at '{}' with client-id '{}'", - m_client.get_server_uri(), m_client.get_client_id()); + logger().info("Connecting to MQTT broker at '{}' with client-id '{}'", + m_client.get_server_uri(), m_client.get_client_id()); + + /* Backward "compatibility warning */ + if (getenv("MQTT_BROKER_URI") != nullptr) { + logger().warn("... ignoring deprecated environment variable MQTT_BROKER_URI -> " + "consider to remove it"); } - m_client.connect(optionsBuilder.finalize())->wait(); + m_client.connect()->wait(); } void disconnect() override { m_client.disconnect()->wait(); } bool isConnected() const override { return m_client.is_connected(); } From 6c1fd59b71f68762eea753b6186b4a461ff33f08 Mon Sep 17 00:00:00 2001 From: BjoernAtBosch Date: Wed, 5 Jul 2023 10:45:04 +0200 Subject: [PATCH 10/16] Apply documentation suggestions from code review Co-authored-by: Dominic Sudy <99014187+doosuu@users.noreply.github.com> --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 917394d9..d779fad9 100644 --- a/README.md +++ b/README.md @@ -78,10 +78,10 @@ You can configure the middleware to be used (i.e. either `dapr` or `native`) via | | `SDV_MIDDLEWARE_TYPE` | `dapr` | Defines the middleware to be used by the app (either `dapr` or `native`) | | Dapr | `SDV_MQTT_ADDRESS` | `localhost:1883` | Address of the MQTT broker - needs to be set explicitly as the C++ SDK does not support Dapr PubSub, yet -| | `SEATSERVICE_DAPR_APP_ID` | `seatservice` | Application id used by Dapr to identifying the application providing the seat service -| | `VEHICLEDATABROKER_DAPR_APP_ID` | `vehicledatabroker` | Application id used by Dapr to identifying the application providing the seat service +| | `SEATSERVICE_DAPR_APP_ID` | `seatservice` | Application id used by Dapr to discover the application providing the seat service +| | `VEHICLEDATABROKER_DAPR_APP_ID` | `vehicledatabroker` | Application id used by Dapr to discover the application providing the Kuksa (Vehicle) Data Broker | | (`DAPR_GRPC_PORT`) | - | Usually, `DAPR_GRPC_PORT` and `DAPR_HTTP_PORT` don't need to be configured manually. They are set by `dapr run` when starting the app and its sidecar. -| | (`DAPR_HTTP_PORT`) | - | If you need to start your app separately, please make sure these ports are set equally to the app and to its sidecar (see above). +| | (`DAPR_HTTP_PORT`) | - | If you need to start your app separately, please make sure these environment variables are available to both the app and its sidecar with identical values (see above). | | | native | `SDV_MQTT_ADDRESS` | `localhost:1883` | Address of the MQTT broker | | `SDV_SEATSERVICE_ADDRESS` | - | Address of the seat service From 1f5cba80694e25dbcf906aa44ea487a740df50f0 Mon Sep 17 00:00:00 2001 From: BjoernAtBosch Date: Thu, 6 Jul 2023 15:49:17 +0200 Subject: [PATCH 11/16] Review fixes (wip) --- README.md | 2 +- sdk/include/sdk/Config.h | 31 ----------- sdk/include/sdk/IPubSubClient.h | 15 ++++++ sdk/include/sdk/Utils.h | 34 +++++++++++- sdk/include/sdk/dapr/DaprSupport.h | 3 +- sdk/src/sdk/Model.cpp | 6 +-- sdk/src/sdk/VehicleApp.cpp | 8 +-- sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp | 6 +-- sdk/src/sdk/middleware/DaprMiddleware.cpp | 14 +++-- sdk/src/sdk/middleware/NativeMiddleware.cpp | 24 +++++---- sdk/src/sdk/pubsub/MqttPubSubClient.cpp | 3 +- sdk/tests/unit/CMakeLists.txt | 1 + sdk/tests/unit/DaprMiddleware_tests.cpp | 13 +++-- sdk/tests/unit/Middleware_tests.cpp | 15 +++--- sdk/tests/unit/NativeMiddleware_tests.cpp | 7 ++- sdk/tests/unit/TestBaseUsingEnvVars.cpp | 56 ++++++++++++++++++++ sdk/tests/unit/TestBaseUsingEnvVars.h | 50 +++++++++++++++++ 17 files changed, 209 insertions(+), 79 deletions(-) delete mode 100644 sdk/include/sdk/Config.h create mode 100644 sdk/tests/unit/TestBaseUsingEnvVars.cpp create mode 100644 sdk/tests/unit/TestBaseUsingEnvVars.h diff --git a/README.md b/README.md index d779fad9..7c0b3a28 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ You can configure the middleware to be used (i.e. either `dapr` or `native`) via | | `SEATSERVICE_DAPR_APP_ID` | `seatservice` | Application id used by Dapr to discover the application providing the seat service | | `VEHICLEDATABROKER_DAPR_APP_ID` | `vehicledatabroker` | Application id used by Dapr to discover the application providing the Kuksa (Vehicle) Data Broker | | (`DAPR_GRPC_PORT`) | - | Usually, `DAPR_GRPC_PORT` and `DAPR_HTTP_PORT` don't need to be configured manually. They are set by `dapr run` when starting the app and its sidecar. -| | (`DAPR_HTTP_PORT`) | - | If you need to start your app separately, please make sure these environment variables are available to both the app and its sidecar with identical values (see above). +| | (`DAPR_HTTP_PORT`) | - | If you need to start your app separately, please make sure these environment variables are equally available to both the app and its sidecar with identical values (see above). | | | native | `SDV_MQTT_ADDRESS` | `localhost:1883` | Address of the MQTT broker | | `SDV_SEATSERVICE_ADDRESS` | - | Address of the seat service diff --git a/sdk/include/sdk/Config.h b/sdk/include/sdk/Config.h deleted file mode 100644 index 3e110996..00000000 --- a/sdk/include/sdk/Config.h +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2023 Robert Bosch GmbH - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://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. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef VEHICLE_APP_SDK_CONFIG_H -#define VEHICLE_APP_SDK_CONFIG_H - -#include "middleware/Middleware.h" - -namespace velocitas { - -class Config { -public: - static Middleware& getMiddleware() { return Middleware::getInstance(); } -}; - -} // namespace velocitas - -#endif // VEHICLE_APP_SDK_CONFIG_H diff --git a/sdk/include/sdk/IPubSubClient.h b/sdk/include/sdk/IPubSubClient.h index 73253528..7eb24068 100644 --- a/sdk/include/sdk/IPubSubClient.h +++ b/sdk/include/sdk/IPubSubClient.h @@ -30,7 +30,22 @@ namespace velocitas { */ class IPubSubClient { public: + /** + * @brief Create an instance of a pub/sub client according to the defined middleware + * configuration + * + * @param clientId used to identify the client at the pub/sub server + * @return std::shared_ptr reference to the created pub/sub client + */ static std::shared_ptr createInstance(const std::string& clientId); + + /** + * @brief Create a new instance of a MQTT client connecting to a broker at the specified address + * + * @param brokerUri address of the MQTT broker to connect to + * @param clientId used to identify the client at the MQTT broker + * @return std::shared_ptr reference to the created MQTT client + */ static std::shared_ptr createInstance(const std::string& brokerUri, const std::string& clientId); diff --git a/sdk/include/sdk/Utils.h b/sdk/include/sdk/Utils.h index 11eb712c..00b9baaf 100644 --- a/sdk/include/sdk/Utils.h +++ b/sdk/include/sdk/Utils.h @@ -37,14 +37,46 @@ std::string getEnvVar(const std::string& varName, const std::string& defaultValu */ class StringUtils final { public: + /** + * @brief Convert the passed string to lowercase + * + * @param str string to be converted + * @return std::string having all uppercase letters contained in the passed string converted to + * lowercase + */ static std::string toLower(const std::string& str); + + /** + * @brief Convert the passed string to uppercase + * + * @param str string to be converted + * @return std::string having all lowercase letters contained in the passed string converted to + * uppercase + */ static std::string toUpper(const std::string& str); + /** + * @brief Concatenate the strings of the passed vector by adding the passed separator between + * each two of the array elements. + * + * Examples: + * + * stringVector | separator | result + * -------------------------|------------|-------------------- + * [] (empty vector) | don't care | "" + * ["hello"] | don't care | "hello" + * ["hello", "world", "eh"] | ", " | "hello, world, eh" + * ["single", "ton"] | "" | "singleton" + * + * @param stringVector vector of strings to be concatenated + * @param separator string to be put between each two vector elements + * @return std::string containing the joined contents + */ static std::string join(const std::vector& stringVector, const std::string& separator); private: - StringUtils() = default; + StringUtils() = delete; }; } // namespace velocitas diff --git a/sdk/include/sdk/dapr/DaprSupport.h b/sdk/include/sdk/dapr/DaprSupport.h index 4d2dd1c7..c7556ff7 100644 --- a/sdk/include/sdk/dapr/DaprSupport.h +++ b/sdk/include/sdk/dapr/DaprSupport.h @@ -20,6 +20,7 @@ namespace velocitas::dapr { * @brief Wait for the dapr sidecar to become available. * */ -void waitForSidecar(); +[[deprecated("Don't call - this is handled by middleware internally. Will be removed soon!")]] void +waitForSidecar(); } // namespace velocitas::dapr diff --git a/sdk/src/sdk/Model.cpp b/sdk/src/sdk/Model.cpp index be5c7099..4978f1bc 100644 --- a/sdk/src/sdk/Model.cpp +++ b/sdk/src/sdk/Model.cpp @@ -15,16 +15,16 @@ */ #include "sdk/Model.h" -#include "sdk/Config.h" +#include "sdk/middleware/Middleware.h" namespace velocitas { std::string Service::getLocation() const { - return Config::getMiddleware().getServiceLocation(getName()); + return Middleware::getInstance().getServiceLocation(getName()); } Middleware::Metadata Service::getMiddlewareMetadata() const { - return Config::getMiddleware().getMetadata(getName()); + return Middleware::getInstance().getMetadata(getName()); } } // namespace velocitas diff --git a/sdk/src/sdk/VehicleApp.cpp b/sdk/src/sdk/VehicleApp.cpp index 8c34ce16..3e7a070b 100644 --- a/sdk/src/sdk/VehicleApp.cpp +++ b/sdk/src/sdk/VehicleApp.cpp @@ -16,10 +16,10 @@ #include "sdk/VehicleApp.h" -#include "sdk/Config.h" #include "sdk/IPubSubClient.h" #include "sdk/Logger.h" #include "sdk/VehicleModelContext.h" +#include "sdk/middleware/Middleware.h" #include "sdk/vdb/IVehicleDataBrokerClient.h" #include @@ -41,8 +41,8 @@ VehicleApp::VehicleApp(std::shared_ptr vdbClient, void VehicleApp::run() { logger().info("Running App..."); - Config::getMiddleware().start(); - Config::getMiddleware().waitUntilReady(); + Middleware::getInstance().start(); + Middleware::getInstance().waitUntilReady(); m_pubSubClient->connect(); onStart(); @@ -59,7 +59,7 @@ void VehicleApp::stop() { onStop(); m_pubSubClient->disconnect(); - Config::getMiddleware().stop(); + Middleware::getInstance().stop(); } AsyncSubscriptionPtr_t VehicleApp::subscribeToTopic(const std::string& topic) { diff --git a/sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp b/sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp index 103bddc6..eafb7096 100644 --- a/sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp +++ b/sdk/src/sdk/grpc/VehicleDataBrokerClient.cpp @@ -16,7 +16,6 @@ #include "sdk/grpc/VehicleDataBrokerClient.h" -#include "sdk/Config.h" #include "sdk/DataPointValue.h" #include "sdk/Exceptions.h" #include "sdk/Job.h" @@ -25,6 +24,7 @@ #include "sdk/grpc/AsyncGrpcFacade.h" #include "sdk/grpc/BrokerAsyncGrpcFacade.h" #include "sdk/grpc/GrpcDataPointValueProvider.h" +#include "sdk/middleware/Middleware.h" #include #include @@ -41,7 +41,7 @@ VehicleDataBrokerClient::VehicleDataBrokerClient(const std::string& vdbAddress, logger().info("Connecting to data broker service '{}' via '{}'", vdbServiceName, vdbAddress); m_asyncBrokerFacade = std::make_shared( grpc::CreateChannel(vdbAddress, grpc::InsecureChannelCredentials())); - Middleware::Metadata metadata = Config::getMiddleware().getMetadata(vdbServiceName); + Middleware::Metadata metadata = Middleware::getInstance().getMetadata(vdbServiceName); m_asyncBrokerFacade->setContextModifier([metadata](auto& context) { for (auto metadatum : metadata) { context.AddMetadata(metadatum.first, metadatum.second); @@ -50,7 +50,7 @@ VehicleDataBrokerClient::VehicleDataBrokerClient(const std::string& vdbAddress, } VehicleDataBrokerClient::VehicleDataBrokerClient(const std::string& vdbServiceName) - : VehicleDataBrokerClient(Config::getMiddleware().getServiceLocation(vdbServiceName), + : VehicleDataBrokerClient(Middleware::getInstance().getServiceLocation(vdbServiceName), vdbServiceName) {} VehicleDataBrokerClient::~VehicleDataBrokerClient() {} diff --git a/sdk/src/sdk/middleware/DaprMiddleware.cpp b/sdk/src/sdk/middleware/DaprMiddleware.cpp index 64679f9f..8259aebc 100644 --- a/sdk/src/sdk/middleware/DaprMiddleware.cpp +++ b/sdk/src/sdk/middleware/DaprMiddleware.cpp @@ -31,12 +31,12 @@ namespace velocitas { -static constexpr char const* ENV_DAPR_GRPC_PORT = "DAPR_GRPC_PORT"; -static constexpr char const* ENV_DAPR_HTTP_PORT = "DAPR_HTTP_PORT"; +namespace { -static constexpr char const* DAPR_APP_ID_KEY = "dapr-app-id"; +constexpr char const* ENV_DAPR_GRPC_PORT = "DAPR_GRPC_PORT"; +constexpr char const* ENV_DAPR_HTTP_PORT = "DAPR_HTTP_PORT"; -namespace dapr { +constexpr char const* DAPR_APP_ID_KEY = "dapr-app-id"; void waitForSidecar() { auto sidecarHttpPort = getEnvVar(ENV_DAPR_HTTP_PORT); @@ -64,9 +64,13 @@ void waitForSidecar() { } } +} // anonymous namespace + +namespace dapr { +void waitForSidecar() { velocitas::waitForSidecar(); } } // namespace dapr -void DaprMiddleware::waitUntilReady() { dapr::waitForSidecar(); } +void DaprMiddleware::waitUntilReady() { waitForSidecar(); } std::string DaprMiddleware::getServiceLocation(const std::string& serviceName) const { std::ignore = serviceName; diff --git a/sdk/src/sdk/middleware/NativeMiddleware.cpp b/sdk/src/sdk/middleware/NativeMiddleware.cpp index 7c1a292f..750d4dce 100644 --- a/sdk/src/sdk/middleware/NativeMiddleware.cpp +++ b/sdk/src/sdk/middleware/NativeMiddleware.cpp @@ -48,17 +48,19 @@ static std::string getServiceEnvVarName(const std::string& serviceName) { std::string NativeMiddleware::getServiceLocation(const std::string& serviceName) const { auto envVarName = getServiceEnvVarName(serviceName); auto serviceAddress = getEnvVar(envVarName); - if (serviceAddress.empty()) { - serviceAddress = getDefaultLocation(serviceName); - if (serviceAddress.empty()) { - logger().error( - "Env variable '{}' defining location of service '{}' not set. Please define!", - envVarName, serviceName); - } else { - logger().warn( - "Env variable '{}' defining location of service '{}' not set. Taking default: '{}'", - envVarName, serviceName, serviceAddress); - } + if (!serviceAddress.empty()) { + return serviceAddress; + } + + serviceAddress = getDefaultLocation(serviceName); + if (!serviceAddress.empty()) { + logger().warn( + "Env variable '{}' defining location of service '{}' not set. Taking default: '{}'", + envVarName, serviceName, serviceAddress); + } else { + logger().error( + "Env variable '{}' defining location of service '{}' not set. Please define!", + envVarName, serviceName); } return serviceAddress; } diff --git a/sdk/src/sdk/pubsub/MqttPubSubClient.cpp b/sdk/src/sdk/pubsub/MqttPubSubClient.cpp index 2e2547ef..623fe0be 100644 --- a/sdk/src/sdk/pubsub/MqttPubSubClient.cpp +++ b/sdk/src/sdk/pubsub/MqttPubSubClient.cpp @@ -14,7 +14,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "sdk/Config.h" #include "sdk/IPubSubClient.h" #include "sdk/Logger.h" #include "sdk/Status.h" @@ -98,7 +97,7 @@ class MqttPubSubClient : public IPubSubClient, private mqtt::callback { }; std::shared_ptr IPubSubClient::createInstance(const std::string& clientId) { - return Config::getMiddleware().createPubSubClient(clientId); + return Middleware::getInstance().createPubSubClient(clientId); } std::shared_ptr IPubSubClient::createInstance(const std::string& brokerUri, diff --git a/sdk/tests/unit/CMakeLists.txt b/sdk/tests/unit/CMakeLists.txt index 66959233..81a0a5f7 100644 --- a/sdk/tests/unit/CMakeLists.txt +++ b/sdk/tests/unit/CMakeLists.txt @@ -18,6 +18,7 @@ add_executable(${TARGET_NAME} VehicleDataBrokerClient_tests.cpp QueryBuilder_tests.cpp #PubSub_tests.cpp + TestBaseUsingEnvVars.cpp ) add_dependencies(${TARGET_NAME} diff --git a/sdk/tests/unit/DaprMiddleware_tests.cpp b/sdk/tests/unit/DaprMiddleware_tests.cpp index 9e8070ab..5fb78bcd 100644 --- a/sdk/tests/unit/DaprMiddleware_tests.cpp +++ b/sdk/tests/unit/DaprMiddleware_tests.cpp @@ -16,13 +16,12 @@ #include "sdk/middleware/DaprMiddleware.h" +#include "TestBaseUsingEnvVars.h" #include -#include - using namespace velocitas; -class Test_DaprMiddleware : public ::testing::Test { +class Test_DaprMiddleware : public TestUsingEnvVars { protected: Middleware& getCut() { return m_cut; } @@ -42,25 +41,25 @@ TEST_F(Test_DaprMiddleware, createPubSubClient__validPointer) { } TEST_F(Test_DaprMiddleware, getServiceLocation_envDaprGrpcPortSet_contentOfEnvVar) { - ::setenv("DAPR_GRPC_PORT", "12345", /*overwrite=*/true); + setEnvVar("DAPR_GRPC_PORT", "12345"); auto serviceLocation = getCut().getServiceLocation("SomeService"); EXPECT_EQ("localhost:12345", serviceLocation); } TEST_F(Test_DaprMiddleware, getServiceLocation_envDaprGrpcPortNotSet_throwsRuntimeError) { - ::unsetenv("DAPR_GRPC_PORT"); + unsetEnvVar("DAPR_GRPC_PORT"); EXPECT_THROW(getCut().getServiceLocation("UnknownService"), std::runtime_error); } TEST_F(Test_DaprMiddleware, getMetadata_envVarWithApppIdSet_metadataWithContentOfEnvVar) { - ::setenv("SOMESERVICE_DAPR_APP_ID", "some-explicit-app-id", /*overwrite=*/true); + setEnvVar("SOMESERVICE_DAPR_APP_ID", "some-explicit-app-id"); Middleware::Metadata metadata = getCut().getMetadata("SomeService"); Middleware::Metadata expectedMetadata = {{"dapr-app-id", "some-explicit-app-id"}}; EXPECT_EQ(expectedMetadata, metadata); } TEST_F(Test_DaprMiddleware, getMetadata_envVarWithApppIdNotSet_metadataWithDefaultAppId) { - ::unsetenv("SOMESERVICE_DAPR_APP_ID"); + unsetEnvVar("SOMESERVICE_DAPR_APP_ID"); Middleware::Metadata metadata = getCut().getMetadata("SomeService"); Middleware::Metadata expectedMetadata = {{"dapr-app-id", "someservice"}}; EXPECT_EQ(expectedMetadata, metadata); diff --git a/sdk/tests/unit/Middleware_tests.cpp b/sdk/tests/unit/Middleware_tests.cpp index 281a80a4..39ad09d6 100644 --- a/sdk/tests/unit/Middleware_tests.cpp +++ b/sdk/tests/unit/Middleware_tests.cpp @@ -14,6 +14,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include "TestBaseUsingEnvVars.h" #include #include @@ -25,34 +26,36 @@ using namespace velocitas; -TEST(Test_Middleware, getTypeDefiningEnvVarName__noneEmptyString) { +class Test_Middleware : public TestUsingEnvVars {}; + +TEST_F(Test_Middleware, getTypeDefiningEnvVarName__noneEmptyString) { EXPECT_FALSE(Middleware::getTypeDefiningEnvVarName().empty()); } -TEST(Test_Middleware, getInstantance_envVarGloballySetToNative_TypeIdIsNative) { +TEST_F(Test_Middleware, getInstantance_envVarGloballySetToNative_TypeIdIsNative) { const Middleware& middleware = Middleware::getInstance(); EXPECT_EQ(NativeMiddleware::TYPE_ID, middleware.getTypeId()); } -TEST(Test_Middleware, instantiate_envVarNotSet_typeIdIsDapr) { +TEST_F(Test_Middleware, instantiate_envVarNotSet_typeIdIsDapr) { ::unsetenv(Middleware::getTypeDefiningEnvVarName().c_str()); auto middleware = Middleware::instantiate(); EXPECT_EQ(DaprMiddleware::TYPE_ID, middleware->getTypeId()); } -TEST(Test_Middleware, instantiate_envVarSetToDapr_typeIdIsDapr) { +TEST_F(Test_Middleware, instantiate_envVarSetToDapr_typeIdIsDapr) { ::setenv(Middleware::getTypeDefiningEnvVarName().c_str(), DaprMiddleware::TYPE_ID, /*overwrite=*/true); EXPECT_EQ(DaprMiddleware::TYPE_ID, Middleware::instantiate()->getTypeId()); } -TEST(Test_Middleware, instantiate_envVarSetToNative_TypeIdIsNative) { +TEST_F(Test_Middleware, instantiate_envVarSetToNative_TypeIdIsNative) { ::setenv(Middleware::getTypeDefiningEnvVarName().c_str(), NativeMiddleware::TYPE_ID, /*overwrite=*/true); EXPECT_EQ(NativeMiddleware::TYPE_ID, Middleware::instantiate()->getTypeId()); } -TEST(Test_Middleware, instantiate_envVarSetToUndefined_throwRuntimeError) { +TEST_F(Test_Middleware, instantiate_envVarSetToUndefined_throwRuntimeError) { ::setenv(Middleware::getTypeDefiningEnvVarName().c_str(), "something undefined", /*overwrite=*/true); EXPECT_THROW(Middleware::instantiate(), std::runtime_error); diff --git a/sdk/tests/unit/NativeMiddleware_tests.cpp b/sdk/tests/unit/NativeMiddleware_tests.cpp index e065ae64..b86291dc 100644 --- a/sdk/tests/unit/NativeMiddleware_tests.cpp +++ b/sdk/tests/unit/NativeMiddleware_tests.cpp @@ -16,13 +16,12 @@ #include "sdk/middleware/NativeMiddleware.h" +#include "TestBaseUsingEnvVars.h" #include -#include - using namespace velocitas; -class Test_NativeMiddleware : public ::testing::Test { +class Test_NativeMiddleware : public TestUsingEnvVars { protected: Middleware& getCut() { return m_cut; } @@ -47,7 +46,7 @@ TEST_F(Test_NativeMiddleware, getServiceLocation_envVarNotSet_emptyString) { } TEST_F(Test_NativeMiddleware, getServiceLocation_envVarSet_contentOfEnvVar) { - ::setenv("SDV_SOMESERVICE_ADDRESS", "some-service-address", /*overwrite=*/true); + setEnvVar("SDV_SOMESERVICE_ADDRESS", "some-service-address"); auto serviceLocation = getCut().getServiceLocation("SomeService"); EXPECT_EQ("some-service-address", serviceLocation); } diff --git a/sdk/tests/unit/TestBaseUsingEnvVars.cpp b/sdk/tests/unit/TestBaseUsingEnvVars.cpp new file mode 100644 index 00000000..6f17f4d6 --- /dev/null +++ b/sdk/tests/unit/TestBaseUsingEnvVars.cpp @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2023 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "TestBaseUsingEnvVars.h" + +#include +#include + +namespace velocitas { + +void TestUsingEnvVars::checkToStoreVariableState(const std::string& varName) { + if (m_envVarsToRestore.count(varName) > 0 || m_envVarsToUnset.count(varName) > 0) { + return; + } + + const char* content = ::getenv(varName.c_str()); + if (content) { + m_envVarsToRestore[varName] = *content; + } else { + m_envVarsToUnset.insert(varName); + } +} + +void TestUsingEnvVars::setEnvVar(const std::string& varName, const std::string& content) { + checkToStoreVariableState(varName); + ::setenv(varName.c_str(), content.c_str(), /*replace=*/true); +} + +void TestUsingEnvVars::unsetEnvVar(const std::string& varName) { + checkToStoreVariableState(varName); + ::unsetenv(varName.c_str()); +} + +void TestUsingEnvVars::TearDown() { + for (auto varToUnset : m_envVarsToUnset) { + ::unsetenv(varToUnset.c_str()); + } + for (auto varToRestore : m_envVarsToRestore) { + ::setenv(varToRestore.first.c_str(), varToRestore.second.c_str(), /*replace=*/true); + } +} + +} // namespace velocitas diff --git a/sdk/tests/unit/TestBaseUsingEnvVars.h b/sdk/tests/unit/TestBaseUsingEnvVars.h new file mode 100644 index 00000000..a374eb6f --- /dev/null +++ b/sdk/tests/unit/TestBaseUsingEnvVars.h @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2023 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef VEHICLE_APP_SDK_TESTUSINGENVVARS_H +#define VEHICLE_APP_SDK_TESTUSINGENVVARS_H + +#include +#include +#include + +#include + +namespace velocitas { + +class TestUsingEnvVars : public ::testing::Test { +public: + ~TestUsingEnvVars() = default; + +protected: + TestUsingEnvVars() = default; + + // Tears down the test fixture. + virtual void TearDown() override; + + void setEnvVar(const std::string& varName, const std::string& content); + void unsetEnvVar(const std::string& varName); + +private: + void checkToStoreVariableState(const std::string& varName); + + std::unordered_map m_envVarsToRestore; + std::unordered_set m_envVarsToUnset; +}; + +} // namespace velocitas + +#endif // VEHICLE_APP_SDK_TESTUSINGENVVARS_H From 052364195e4b40a79022ff4c9e1eb5a350376ec2 Mon Sep 17 00:00:00 2001 From: BjoernAtBosch Date: Thu, 6 Jul 2023 21:30:38 +0200 Subject: [PATCH 12/16] Further review fixes --- sdk/src/sdk/middleware/DaprMiddleware.cpp | 40 ++++++++++++----------- sdk/tests/unit/Middleware_tests.cpp | 13 +++----- sdk/tests/unit/TestBaseUsingEnvVars.h | 23 ++++++++++++- 3 files changed, 47 insertions(+), 29 deletions(-) diff --git a/sdk/src/sdk/middleware/DaprMiddleware.cpp b/sdk/src/sdk/middleware/DaprMiddleware.cpp index 8259aebc..beec4e26 100644 --- a/sdk/src/sdk/middleware/DaprMiddleware.cpp +++ b/sdk/src/sdk/middleware/DaprMiddleware.cpp @@ -41,26 +41,28 @@ constexpr char const* DAPR_APP_ID_KEY = "dapr-app-id"; void waitForSidecar() { auto sidecarHttpPort = getEnvVar(ENV_DAPR_HTTP_PORT); if (sidecarHttpPort.empty()) { - logger().warn("dapr: env {} not set. Continuing without sidecar health check ...", - ENV_DAPR_HTTP_PORT); - } else { - logger().info("dapr: env {} set. Waiting for sidecar at port {} ...", ENV_DAPR_HTTP_PORT, - sidecarHttpPort); - for (auto success = false; !success;) { - try { - constexpr auto STATUS_NO_CONTENT{204}; - logger().info("dapr: Requesting side car health endpoint..."); - - const auto response = cpr::Get( - cpr::Url(fmt::format("http://localhost:{}/v1.0/healthz", sidecarHttpPort))); - success = response.status_code == STATUS_NO_CONTENT; - logger().debug("dapr: Health endpoint returned status code: {}, {}", - response.status_code, response.text); - } catch (const std::exception& e) { - logger().warn("dapr: Exception occurred requesting health endpoint: {}", e.what()); - } - std::this_thread::sleep_for(std::chrono::seconds(1)); + logger().error("dapr: env {} not set. Cannot check presence of sidecar!", + ENV_DAPR_HTTP_PORT); + throw std::runtime_error( + fmt::format("env {} not set! Cannot check presence of sidecar!", ENV_DAPR_HTTP_PORT)); + } + + logger().info("dapr: env {} set. Waiting for sidecar at port {} ...", ENV_DAPR_HTTP_PORT, + sidecarHttpPort); + for (auto success = false; !success;) { + try { + constexpr auto STATUS_NO_CONTENT{204}; + logger().info("dapr: Requesting side car health endpoint..."); + + const auto response = cpr::Get( + cpr::Url(fmt::format("http://localhost:{}/v1.0/healthz", sidecarHttpPort))); + success = response.status_code == STATUS_NO_CONTENT; + logger().debug("dapr: Health endpoint returned status code: {}, {}", + response.status_code, response.text); + } catch (const std::exception& e) { + logger().warn("dapr: Exception occurred requesting health endpoint: {}", e.what()); } + std::this_thread::sleep_for(std::chrono::seconds(1)); } } diff --git a/sdk/tests/unit/Middleware_tests.cpp b/sdk/tests/unit/Middleware_tests.cpp index 39ad09d6..2ae70455 100644 --- a/sdk/tests/unit/Middleware_tests.cpp +++ b/sdk/tests/unit/Middleware_tests.cpp @@ -17,8 +17,6 @@ #include "TestBaseUsingEnvVars.h" #include -#include - #define private public #include "sdk/middleware/DaprMiddleware.h" #include "sdk/middleware/Middleware.h" @@ -38,25 +36,22 @@ TEST_F(Test_Middleware, getInstantance_envVarGloballySetToNative_TypeIdIsNative) } TEST_F(Test_Middleware, instantiate_envVarNotSet_typeIdIsDapr) { - ::unsetenv(Middleware::getTypeDefiningEnvVarName().c_str()); + unsetEnvVar(Middleware::getTypeDefiningEnvVarName()); auto middleware = Middleware::instantiate(); EXPECT_EQ(DaprMiddleware::TYPE_ID, middleware->getTypeId()); } TEST_F(Test_Middleware, instantiate_envVarSetToDapr_typeIdIsDapr) { - ::setenv(Middleware::getTypeDefiningEnvVarName().c_str(), DaprMiddleware::TYPE_ID, - /*overwrite=*/true); + setEnvVar(Middleware::getTypeDefiningEnvVarName(), DaprMiddleware::TYPE_ID); EXPECT_EQ(DaprMiddleware::TYPE_ID, Middleware::instantiate()->getTypeId()); } TEST_F(Test_Middleware, instantiate_envVarSetToNative_TypeIdIsNative) { - ::setenv(Middleware::getTypeDefiningEnvVarName().c_str(), NativeMiddleware::TYPE_ID, - /*overwrite=*/true); + setEnvVar(Middleware::getTypeDefiningEnvVarName(), NativeMiddleware::TYPE_ID); EXPECT_EQ(NativeMiddleware::TYPE_ID, Middleware::instantiate()->getTypeId()); } TEST_F(Test_Middleware, instantiate_envVarSetToUndefined_throwRuntimeError) { - ::setenv(Middleware::getTypeDefiningEnvVarName().c_str(), "something undefined", - /*overwrite=*/true); + setEnvVar(Middleware::getTypeDefiningEnvVarName(), "something undefined"); EXPECT_THROW(Middleware::instantiate(), std::runtime_error); } diff --git a/sdk/tests/unit/TestBaseUsingEnvVars.h b/sdk/tests/unit/TestBaseUsingEnvVars.h index a374eb6f..5c501181 100644 --- a/sdk/tests/unit/TestBaseUsingEnvVars.h +++ b/sdk/tests/unit/TestBaseUsingEnvVars.h @@ -25,6 +25,12 @@ namespace velocitas { +/** + * @brief Base class for test cases where you need to set or unset environment variables + * + * It provides a setEnvVar and a unsetEnvVar function to be used for this by your test case. + * After the test case is finished the original state of the changed variables is restored. * + */ class TestUsingEnvVars : public ::testing::Test { public: ~TestUsingEnvVars() = default; @@ -32,10 +38,25 @@ class TestUsingEnvVars : public ::testing::Test { protected: TestUsingEnvVars() = default; - // Tears down the test fixture. + /** + * @brief Restores the state of the set and unset variables as it was before executing the test + * case + */ virtual void TearDown() override; + /** + * @brief Set the an environment variable to the specified value + * + * @param varName Name of the variable to be set + * @param content Value to be set + */ void setEnvVar(const std::string& varName, const std::string& content); + + /** + * @brief Unset a possibly existing environment variable + * + * @param varName Name of the variable to be unset + */ void unsetEnvVar(const std::string& varName); private: From df16543cd4dbd517410ee3f7fa654fe9cbaab4c6 Mon Sep 17 00:00:00 2001 From: BjoernAtBosch Date: Thu, 6 Jul 2023 21:34:51 +0200 Subject: [PATCH 13/16] Update NOTICE-3RD-PARTY-CONTENT.md --- NOTICE-3RD-PARTY-CONTENT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NOTICE-3RD-PARTY-CONTENT.md b/NOTICE-3RD-PARTY-CONTENT.md index 0e20ef8e..0da234a6 100644 --- a/NOTICE-3RD-PARTY-CONTENT.md +++ b/NOTICE-3RD-PARTY-CONTENT.md @@ -18,7 +18,7 @@ |identify|2.5.24|MIT| |idna|3.4|BSD| |Jinja2|3.1.2|New BSD| -|lxml|4.9.2|New BSD| +|lxml|4.9.3|New BSD| |MarkupSafe|2.1.3|New BSD| |node-semver|0.6.1|MIT| |nodeenv|1.8.0|BSD| From 9a5aa55cd0ad6281a13288aa97f2a9445c374b99 Mon Sep 17 00:00:00 2001 From: BjoernAtBosch Date: Fri, 7 Jul 2023 09:45:45 +0200 Subject: [PATCH 14/16] Linter fixes --- sdk/tests/unit/TestBaseUsingEnvVars.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/tests/unit/TestBaseUsingEnvVars.h b/sdk/tests/unit/TestBaseUsingEnvVars.h index 5c501181..e8b5d11b 100644 --- a/sdk/tests/unit/TestBaseUsingEnvVars.h +++ b/sdk/tests/unit/TestBaseUsingEnvVars.h @@ -42,7 +42,7 @@ class TestUsingEnvVars : public ::testing::Test { * @brief Restores the state of the set and unset variables as it was before executing the test * case */ - virtual void TearDown() override; + void TearDown() override; /** * @brief Set the an environment variable to the specified value From 7e572ac15111cc618b79ca23ed2e0ab4677346b4 Mon Sep 17 00:00:00 2001 From: BjoernAtBosch Date: Fri, 7 Jul 2023 09:51:06 +0200 Subject: [PATCH 15/16] Update NOTICE-3RD-PARTY-CONTENT.md --- NOTICE-3RD-PARTY-CONTENT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NOTICE-3RD-PARTY-CONTENT.md b/NOTICE-3RD-PARTY-CONTENT.md index 0da234a6..8f76972c 100644 --- a/NOTICE-3RD-PARTY-CONTENT.md +++ b/NOTICE-3RD-PARTY-CONTENT.md @@ -23,7 +23,7 @@ |node-semver|0.6.1|MIT| |nodeenv|1.8.0|BSD| |patch-ng|1.17.4|MIT| -|platformdirs|3.8.0|MIT| +|platformdirs|3.8.1|MIT| |pluginbase|1.0.1|BSD| |pre-commit|2.20.0|MIT| |Pygments|2.15.1|Simplified BSD| From 549daf8c8043bfcc2d5cd1f4df739dfb634b8ced Mon Sep 17 00:00:00 2001 From: BjoernAtBosch Date: Fri, 7 Jul 2023 13:26:33 +0200 Subject: [PATCH 16/16] Fix review findings --- sdk/include/sdk/IPubSubClient.h | 3 ++- sdk/include/sdk/Utils.h | 4 ++-- sdk/include/sdk/middleware/Middleware.h | 9 +++++++-- sdk/src/sdk/middleware/DaprMiddleware.cpp | 4 ++-- sdk/src/sdk/middleware/Middleware.cpp | 5 +---- sdk/src/sdk/middleware/NativeMiddleware.cpp | 14 +++++++++----- sdk/tests/unit/DaprMiddleware_tests.cpp | 8 ++++---- sdk/tests/unit/Middleware_tests.cpp | 18 +++++++----------- sdk/tests/unit/NativeMiddleware_tests.cpp | 5 ++--- sdk/tests/unit/TestBaseUsingEnvVars.cpp | 6 +++--- sdk/tests/unit/TestBaseUsingEnvVars.h | 2 +- .../unit/VehicleDataBrokerClient_tests.cpp | 17 +++-------------- sdk/tests/unit/testmain.cpp | 2 +- 13 files changed, 44 insertions(+), 53 deletions(-) diff --git a/sdk/include/sdk/IPubSubClient.h b/sdk/include/sdk/IPubSubClient.h index 7eb24068..5aba26de 100644 --- a/sdk/include/sdk/IPubSubClient.h +++ b/sdk/include/sdk/IPubSubClient.h @@ -40,7 +40,8 @@ class IPubSubClient { static std::shared_ptr createInstance(const std::string& clientId); /** - * @brief Create a new instance of a MQTT client connecting to a broker at the specified address + * @brief Create a new instance of an MQTT client connecting to a broker at the specified + * address * * @param brokerUri address of the MQTT broker to connect to * @param clientId used to identify the client at the MQTT broker diff --git a/sdk/include/sdk/Utils.h b/sdk/include/sdk/Utils.h index 00b9baaf..43c89270 100644 --- a/sdk/include/sdk/Utils.h +++ b/sdk/include/sdk/Utils.h @@ -38,7 +38,7 @@ std::string getEnvVar(const std::string& varName, const std::string& defaultValu class StringUtils final { public: /** - * @brief Convert the passed string to lowercase + * @brief Return the passed string converted to lowercase * * @param str string to be converted * @return std::string having all uppercase letters contained in the passed string converted to @@ -47,7 +47,7 @@ class StringUtils final { static std::string toLower(const std::string& str); /** - * @brief Convert the passed string to uppercase + * @brief Return the passed string converted to uppercase * * @param str string to be converted * @return std::string having all lowercase letters contained in the passed string converted to diff --git a/sdk/include/sdk/middleware/Middleware.h b/sdk/include/sdk/middleware/Middleware.h index 90fb14dd..012d85b1 100644 --- a/sdk/include/sdk/middleware/Middleware.h +++ b/sdk/include/sdk/middleware/Middleware.h @@ -30,6 +30,12 @@ class IPubSubClient; */ class Middleware { public: + /** + * @brief Defines the name of the environment variable used to determine the middleware type to + * be used. + */ + static constexpr char const* TYPE_DEFINING_ENV_VAR_NAME = "SDV_MIDDLEWARE_TYPE"; + /** * @brief Returns a reference to a singelton instance of a concrete middleware class * @@ -40,8 +46,6 @@ class Middleware { return *singleton; } - static std::string getTypeDefiningEnvVarName(); - /** * @brief Get the type identifier of the concrete middleware implementation * @@ -67,6 +71,7 @@ class Middleware { * * @param serviceName Name of the service to get the loaction description for * @return std::string representing the location description + * @throws std::runtime_error if the service location cannot be determined */ virtual std::string getServiceLocation(const std::string& serviceName) const = 0; diff --git a/sdk/src/sdk/middleware/DaprMiddleware.cpp b/sdk/src/sdk/middleware/DaprMiddleware.cpp index beec4e26..67b70d86 100644 --- a/sdk/src/sdk/middleware/DaprMiddleware.cpp +++ b/sdk/src/sdk/middleware/DaprMiddleware.cpp @@ -79,8 +79,8 @@ std::string DaprMiddleware::getServiceLocation(const std::string& serviceName) c auto daprSidecarGrpcPort = getEnvVar(ENV_DAPR_GRPC_PORT); if (daprSidecarGrpcPort.empty()) { - throw std::runtime_error( - fmt::format("{} not set! Cannot connect to Dapr sidecar!", ENV_DAPR_GRPC_PORT)); + throw std::runtime_error(fmt::format( + "{} not set! Cannot determine address of Dapr sidecar!", ENV_DAPR_GRPC_PORT)); } return "localhost:" + daprSidecarGrpcPort; }; diff --git a/sdk/src/sdk/middleware/Middleware.cpp b/sdk/src/sdk/middleware/Middleware.cpp index 5f697a8c..1228f2ac 100644 --- a/sdk/src/sdk/middleware/Middleware.cpp +++ b/sdk/src/sdk/middleware/Middleware.cpp @@ -25,14 +25,11 @@ #include #include -#include namespace velocitas { -std::string Middleware::getTypeDefiningEnvVarName() { return "SDV_MIDDLEWARE_TYPE"; } - std::unique_ptr Middleware::instantiate() { - const std::string middlewareType = StringUtils::toLower(getEnvVar(getTypeDefiningEnvVarName())); + const std::string middlewareType = StringUtils::toLower(getEnvVar(TYPE_DEFINING_ENV_VAR_NAME)); if (middlewareType.empty()) { return std::make_unique(); } else if (middlewareType == NativeMiddleware::TYPE_ID) { diff --git a/sdk/src/sdk/middleware/NativeMiddleware.cpp b/sdk/src/sdk/middleware/NativeMiddleware.cpp index 750d4dce..80af8ccc 100644 --- a/sdk/src/sdk/middleware/NativeMiddleware.cpp +++ b/sdk/src/sdk/middleware/NativeMiddleware.cpp @@ -21,6 +21,8 @@ #include "sdk/Utils.h" #include "sdk/dapr/DaprSupport.h" +#include "fmt/core.h" + #include #include #include @@ -57,12 +59,14 @@ std::string NativeMiddleware::getServiceLocation(const std::string& serviceName) logger().warn( "Env variable '{}' defining location of service '{}' not set. Taking default: '{}'", envVarName, serviceName, serviceAddress); - } else { - logger().error( - "Env variable '{}' defining location of service '{}' not set. Please define!", - envVarName, serviceName); + return serviceAddress; } - return serviceAddress; + + const std::string errorMsg{ + fmt::format("Env variable '{}' defining location of service '{}' not set. Please define!", + envVarName, serviceName)}; + logger().error(errorMsg); + throw std::runtime_error(errorMsg); } std::shared_ptr diff --git a/sdk/tests/unit/DaprMiddleware_tests.cpp b/sdk/tests/unit/DaprMiddleware_tests.cpp index 5fb78bcd..05cd5ad6 100644 --- a/sdk/tests/unit/DaprMiddleware_tests.cpp +++ b/sdk/tests/unit/DaprMiddleware_tests.cpp @@ -53,14 +53,14 @@ TEST_F(Test_DaprMiddleware, getServiceLocation_envDaprGrpcPortNotSet_throwsRunti TEST_F(Test_DaprMiddleware, getMetadata_envVarWithApppIdSet_metadataWithContentOfEnvVar) { setEnvVar("SOMESERVICE_DAPR_APP_ID", "some-explicit-app-id"); - Middleware::Metadata metadata = getCut().getMetadata("SomeService"); - Middleware::Metadata expectedMetadata = {{"dapr-app-id", "some-explicit-app-id"}}; + const Middleware::Metadata metadata = getCut().getMetadata("SomeService"); + const Middleware::Metadata expectedMetadata = {{"dapr-app-id", "some-explicit-app-id"}}; EXPECT_EQ(expectedMetadata, metadata); } TEST_F(Test_DaprMiddleware, getMetadata_envVarWithApppIdNotSet_metadataWithDefaultAppId) { unsetEnvVar("SOMESERVICE_DAPR_APP_ID"); - Middleware::Metadata metadata = getCut().getMetadata("SomeService"); - Middleware::Metadata expectedMetadata = {{"dapr-app-id", "someservice"}}; + const Middleware::Metadata metadata = getCut().getMetadata("SomeService"); + const Middleware::Metadata expectedMetadata = {{"dapr-app-id", "someservice"}}; EXPECT_EQ(expectedMetadata, metadata); } diff --git a/sdk/tests/unit/Middleware_tests.cpp b/sdk/tests/unit/Middleware_tests.cpp index 2ae70455..8889e530 100644 --- a/sdk/tests/unit/Middleware_tests.cpp +++ b/sdk/tests/unit/Middleware_tests.cpp @@ -26,32 +26,28 @@ using namespace velocitas; class Test_Middleware : public TestUsingEnvVars {}; -TEST_F(Test_Middleware, getTypeDefiningEnvVarName__noneEmptyString) { - EXPECT_FALSE(Middleware::getTypeDefiningEnvVarName().empty()); -} - -TEST_F(Test_Middleware, getInstantance_envVarGloballySetToNative_TypeIdIsNative) { +TEST_F(Test_Middleware, getInstance_envVarGloballySetToNative_typeIdIsNative) { const Middleware& middleware = Middleware::getInstance(); EXPECT_EQ(NativeMiddleware::TYPE_ID, middleware.getTypeId()); } -TEST_F(Test_Middleware, instantiate_envVarNotSet_typeIdIsDapr) { - unsetEnvVar(Middleware::getTypeDefiningEnvVarName()); +TEST_F(Test_Middleware, instantiate_envVarNotSet_typeIdDefaultsToDapr) { + unsetEnvVar(Middleware::TYPE_DEFINING_ENV_VAR_NAME); auto middleware = Middleware::instantiate(); EXPECT_EQ(DaprMiddleware::TYPE_ID, middleware->getTypeId()); } TEST_F(Test_Middleware, instantiate_envVarSetToDapr_typeIdIsDapr) { - setEnvVar(Middleware::getTypeDefiningEnvVarName(), DaprMiddleware::TYPE_ID); + setEnvVar(Middleware::TYPE_DEFINING_ENV_VAR_NAME, DaprMiddleware::TYPE_ID); EXPECT_EQ(DaprMiddleware::TYPE_ID, Middleware::instantiate()->getTypeId()); } -TEST_F(Test_Middleware, instantiate_envVarSetToNative_TypeIdIsNative) { - setEnvVar(Middleware::getTypeDefiningEnvVarName(), NativeMiddleware::TYPE_ID); +TEST_F(Test_Middleware, instantiate_envVarSetToNative_typeIdIsNative) { + setEnvVar(Middleware::TYPE_DEFINING_ENV_VAR_NAME, NativeMiddleware::TYPE_ID); EXPECT_EQ(NativeMiddleware::TYPE_ID, Middleware::instantiate()->getTypeId()); } TEST_F(Test_Middleware, instantiate_envVarSetToUndefined_throwRuntimeError) { - setEnvVar(Middleware::getTypeDefiningEnvVarName(), "something undefined"); + setEnvVar(Middleware::TYPE_DEFINING_ENV_VAR_NAME, "something undefined"); EXPECT_THROW(Middleware::instantiate(), std::runtime_error); } diff --git a/sdk/tests/unit/NativeMiddleware_tests.cpp b/sdk/tests/unit/NativeMiddleware_tests.cpp index b86291dc..b45a7259 100644 --- a/sdk/tests/unit/NativeMiddleware_tests.cpp +++ b/sdk/tests/unit/NativeMiddleware_tests.cpp @@ -40,9 +40,8 @@ TEST_F(Test_NativeMiddleware, createPubSubClient__validPointer) { EXPECT_NE(nullptr, pubSubClient.get()); } -TEST_F(Test_NativeMiddleware, getServiceLocation_envVarNotSet_emptyString) { - auto serviceLocation = getCut().getServiceLocation("UnknownService"); - EXPECT_EQ("", serviceLocation); +TEST_F(Test_NativeMiddleware, getServiceLocation_envVarNotSet_throwsRuntimeError) { + EXPECT_THROW(getCut().getServiceLocation("UnknownService"), std::runtime_error); } TEST_F(Test_NativeMiddleware, getServiceLocation_envVarSet_contentOfEnvVar) { diff --git a/sdk/tests/unit/TestBaseUsingEnvVars.cpp b/sdk/tests/unit/TestBaseUsingEnvVars.cpp index 6f17f4d6..8f13bf08 100644 --- a/sdk/tests/unit/TestBaseUsingEnvVars.cpp +++ b/sdk/tests/unit/TestBaseUsingEnvVars.cpp @@ -21,7 +21,7 @@ namespace velocitas { -void TestUsingEnvVars::checkToStoreVariableState(const std::string& varName) { +void TestUsingEnvVars::preserveEnvVarState(const std::string& varName) { if (m_envVarsToRestore.count(varName) > 0 || m_envVarsToUnset.count(varName) > 0) { return; } @@ -35,12 +35,12 @@ void TestUsingEnvVars::checkToStoreVariableState(const std::string& varName) { } void TestUsingEnvVars::setEnvVar(const std::string& varName, const std::string& content) { - checkToStoreVariableState(varName); + preserveEnvVarState(varName); ::setenv(varName.c_str(), content.c_str(), /*replace=*/true); } void TestUsingEnvVars::unsetEnvVar(const std::string& varName) { - checkToStoreVariableState(varName); + preserveEnvVarState(varName); ::unsetenv(varName.c_str()); } diff --git a/sdk/tests/unit/TestBaseUsingEnvVars.h b/sdk/tests/unit/TestBaseUsingEnvVars.h index e8b5d11b..ddbbd99a 100644 --- a/sdk/tests/unit/TestBaseUsingEnvVars.h +++ b/sdk/tests/unit/TestBaseUsingEnvVars.h @@ -60,7 +60,7 @@ class TestUsingEnvVars : public ::testing::Test { void unsetEnvVar(const std::string& varName); private: - void checkToStoreVariableState(const std::string& varName); + void preserveEnvVarState(const std::string& varName); std::unordered_map m_envVarsToRestore; std::unordered_set m_envVarsToUnset; diff --git a/sdk/tests/unit/VehicleDataBrokerClient_tests.cpp b/sdk/tests/unit/VehicleDataBrokerClient_tests.cpp index 72ad07e3..628aac18 100644 --- a/sdk/tests/unit/VehicleDataBrokerClient_tests.cpp +++ b/sdk/tests/unit/VehicleDataBrokerClient_tests.cpp @@ -18,20 +18,9 @@ #include -#include - using namespace velocitas; -class Test_VehicleDataBrokerClient : public ::testing::Test { -protected: - static void SetUpTestSuite() { ::setenv("SDV_MIDDLEWARE_TYPE", "native", /*overwrite=*/true); } - - Test_VehicleDataBrokerClient() - : m_cut("some-broker-service-name") {} - - VehicleDataBrokerClient m_cut; -}; - -TEST_F(Test_VehicleDataBrokerClient, getDatapoints_noConnection_throwsAsyncException) { - EXPECT_THROW(m_cut.getDatapoints({})->await(), AsyncException); +TEST(Test_VehicleDataBrokerClient, getDatapoints_noConnection_throwsAsyncException) { + auto client = VehicleDataBrokerClient("vehicledatabroker"); + EXPECT_THROW(client.getDatapoints({})->await(), AsyncException); } diff --git a/sdk/tests/unit/testmain.cpp b/sdk/tests/unit/testmain.cpp index 25ea676d..4e0cfdd9 100644 --- a/sdk/tests/unit/testmain.cpp +++ b/sdk/tests/unit/testmain.cpp @@ -25,7 +25,7 @@ using namespace velocitas; class SetupMiddlewareSingleton : public ::testing::Environment { public: void SetUp() override { - ::setenv(Middleware::getTypeDefiningEnvVarName().c_str(), NativeMiddleware::TYPE_ID, + ::setenv(Middleware::TYPE_DEFINING_ENV_VAR_NAME, NativeMiddleware::TYPE_ID, /*overwrite=*/true); std::ignore = Middleware::getInstance(); }