├── .clang-format ├── .clang-format-ignore ├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── codeql.yml │ └── systemtest.yml ├── .gitignore ├── 3rd-party ├── CMakeLists.txt └── date │ ├── LICENSE │ ├── README.md │ └── date.h ├── CMakeLists.txt ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── cmake └── InfluxDBConfig.cmake.in ├── conanfile.py ├── include └── InfluxDB │ ├── InfluxDB.h │ ├── InfluxDBBuilder.h │ ├── InfluxDBException.h │ ├── InfluxDBFactory.h │ ├── Point.h │ ├── Proxy.h │ └── Transport.h ├── script ├── ci_build.sh ├── ci_setup.sh ├── ci_testdeploy.sh └── include_library │ ├── CMakeLists.txt │ └── main.cxx ├── src ├── BoostSupport.cxx ├── BoostSupport.h ├── CMakeLists.txt ├── HTTP.cxx ├── HTTP.h ├── InfluxDB.cxx ├── InfluxDBBuilder.cxx ├── InfluxDBFactory.cxx ├── LineProtocol.cxx ├── LineProtocol.h ├── NoBoostSupport.cxx ├── Point.cxx ├── Proxy.cxx ├── TCP.cxx ├── TCP.h ├── UDP.cxx ├── UDP.h ├── UnixSocket.cxx ├── UnixSocket.h └── UriParser.h └── test ├── BoostSupportTest.cxx ├── CMakeLists.txt ├── HttpTest.cxx ├── InfluxDBFactoryTest.cxx ├── InfluxDBTest.cxx ├── LineProtocolTest.cxx ├── NoBoostSupportTest.cxx ├── PointTest.cxx ├── ProxyTest.cxx ├── UriParserTest.cxx ├── mock ├── CMakeLists.txt ├── CprMock.cxx ├── CprMock.h └── TransportMock.h └── system ├── CMakeLists.txt ├── HttpAuthST.cxx ├── InfluxDBST.cxx └── SystemTest.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AccessModifierOffset: '-4' 3 | AllowShortBlocksOnASingleLine: 'false' 4 | AllowShortCaseLabelsOnASingleLine: 'false' 5 | AllowShortFunctionsOnASingleLine: None 6 | AllowShortIfStatementsOnASingleLine: 'false' 7 | AllowShortLoopsOnASingleLine: 'false' 8 | AlwaysBreakTemplateDeclarations: 'true' 9 | BreakBeforeBraces: Allman 10 | ColumnLimit: '0' 11 | FixNamespaceComments: 'false' 12 | IndentCaseLabels: 'true' 13 | IndentWidth: '4' 14 | InsertBraces: 'true' 15 | Language: Cpp 16 | MaxEmptyLinesToKeep: '2' 17 | NamespaceIndentation: All 18 | NamespaceMacros: ['TEST_GROUP'] 19 | PointerAlignment: Left 20 | SortIncludes: 'false' 21 | SpaceAfterCStyleCast: 'true' 22 | SpaceInEmptyParentheses: 'false' 23 | TabWidth: '4' 24 | UseTab: Never 25 | 26 | ... 27 | -------------------------------------------------------------------------------- /.clang-format-ignore: -------------------------------------------------------------------------------- 1 | ./3rd-party/* 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | pull-requests: read 8 | 9 | jobs: 10 | build_linux: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | compiler: 15 | - gcc:15 16 | - gcc:14 17 | - clang:20 18 | - clang:19 19 | boost: 20 | - true 21 | - false 22 | container: 23 | image: "registry.gitlab.com/offa/docker-images/${{ matrix.compiler }}" 24 | name: "${{ matrix.compiler }} (Boost: ${{ matrix.boost }})" 25 | steps: 26 | - uses: actions/checkout@main 27 | - name: Cache Conan Packages 28 | uses: actions/cache@main 29 | with: 30 | path: ~/.conan2/p/ 31 | key: conan-${{ runner.os }}-${{ matrix.compiler }}-boost_${{ matrix.boost }}-${{ hashFiles('conanfile.py') }} 32 | - name: Install dependencies 33 | run: script/ci_setup.sh 34 | - name: Install Boost 35 | if: ${{ matrix.boost == true }} 36 | run: | 37 | add-apt-repository ppa:mhier/libboost-latest 38 | apt-get update 39 | apt-get install -y boost1.83 40 | - name: Build 41 | run: script/ci_build.sh -DINFLUXCXX_WITH_BOOST=${{ matrix.boost }} 42 | - name: Check deployment as cmake subdirectory 43 | run: script/ci_testdeploy.sh -DINFLUXCXX_AS_SUBDIR=ON -DINFLUXCXX_WITH_BOOST=${{ matrix.boost }} 44 | - name: Install 45 | run: cmake --install build 46 | - name: Check installed library 47 | run: script/ci_testdeploy.sh -DINFLUXCXX_AS_SUBDIR=OFF 48 | 49 | 50 | build_windows: 51 | runs-on: windows-latest 52 | strategy: 53 | matrix: 54 | compiler: 55 | - msvc 56 | boost: 57 | - "True" 58 | - "False" 59 | name: "${{ matrix.compiler }} (Boost: ${{ matrix.boost }})" 60 | defaults: 61 | run: 62 | shell: bash 63 | steps: 64 | - uses: actions/checkout@main 65 | - name: Cache Conan Packages 66 | uses: actions/cache@main 67 | with: 68 | path: ~/.conan2/p/ 69 | key: conan-${{ runner.os }}-${{ matrix.compiler }}-boost_${{ matrix.boost }}-${{ hashFiles('conanfile.py') }} 70 | - name: Setup Ninja 71 | uses: turtlesec-no/get-ninja@3e85fb0044ada1440765803dd87c838edf79def8 72 | - name: Setup MSVC 73 | uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 74 | - name: Install dependencies 75 | run: | 76 | pip3 install -U conan 77 | conan profile detect 78 | echo "tools.cmake.cmaketoolchain:generator=Ninja" >> ~/.conan2/global.conf 79 | mkdir build 80 | conan install -o "&":boost=${{ matrix.boost }} -s compiler.cppstd=20 --build=missing . -of build 81 | conan install --build=missing -of build --requires "libcurl/7.87.0" --deployer full_deploy 82 | - name: Build 83 | run: | 84 | export CL="/ID:\a\influxdb-cxx\influxdb-cxx\build\host\libcurl\7.87.0\Release\x86_64\include" 85 | script/ci_build.sh -DBUILD_SHARED_LIBS=OFF -DCMAKE_CXX_FLAGS_INIT=-D_WIN32_WINNT=0x0A00 -DINFLUXCXX_WITH_BOOST=${{ matrix.boost }} 86 | - name: Install 87 | run: cmake --build --preset conan-release --target install 88 | 89 | 90 | build_osx: 91 | runs-on: macOS-latest 92 | name: "mac os x" 93 | steps: 94 | - uses: actions/checkout@main 95 | - name: Cache Conan Packages 96 | uses: actions/cache@main 97 | with: 98 | path: ~/.conan2/p/ 99 | key: conan-${{ runner.os }}-macos-${{ hashFiles('conanfile.py') }} 100 | - name: Install Dependencies 101 | run: | 102 | export HOMEBREW_NO_AUTO_UPDATE=1 103 | export HOMEBREW_NO_INSTALL_CLEANUP=1 104 | brew install boost 105 | pipx install conan 106 | conan profile detect 107 | mkdir build 108 | conan install -o "&":system=True -s compiler.cppstd=20 --build=missing . -of build 109 | - name: Build 110 | run: script/ci_build.sh 111 | - name: Install 112 | run: cmake --install ./build --prefix /tmp/ci_ifdb 113 | 114 | 115 | formatting-check: 116 | name: "formatting" 117 | runs-on: ubuntu-latest 118 | steps: 119 | - uses: actions/checkout@main 120 | - uses: jidicula/clang-format-action@5cc331b319e3ad388e0e16ccae131363f0a82c37 121 | name: "Verify formatting" 122 | with: 123 | clang-format-version: 19 124 | exclude-regex: "/3rd-party/" 125 | 126 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: codeql 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '0 5 * * 3' 8 | 9 | permissions: 10 | contents: read 11 | pull-requests: read 12 | security-events: write 13 | 14 | jobs: 15 | codeql: 16 | runs-on: ubuntu-latest 17 | container: 18 | image: "registry.gitlab.com/offa/docker-images/gcc:15" 19 | name: "CodeQL" 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@main 23 | - name: Install dependencies 24 | run: | 25 | script/ci_setup.sh 26 | add-apt-repository ppa:mhier/libboost-latest 27 | apt-get update 28 | apt-get install -y boost1.83 29 | - name: CodeQL Initialization 30 | uses: github/codeql-action/init@v3 31 | with: 32 | languages: cpp, actions 33 | queries: +security-and-quality 34 | - name: Build 35 | run: script/ci_build.sh 36 | - name: CodeQL Analysis 37 | uses: github/codeql-action/analyze@v3 38 | -------------------------------------------------------------------------------- /.github/workflows/systemtest.yml: -------------------------------------------------------------------------------- 1 | name: system test 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | pull-requests: read 8 | 9 | jobs: 10 | build_linux: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | influx_version: 15 | - 1.8 16 | auth_enabled: 17 | - true 18 | - false 19 | container: 20 | image: "registry.gitlab.com/offa/docker-images/gcc:15" 21 | services: 22 | influxdb: 23 | image: influxdb:${{ matrix.influx_version }} 24 | env: 25 | INFLUXDB_HTTP_AUTH_ENABLED: ${{ matrix.auth_enabled }} 26 | INFLUXDB_ADMIN_USER: st_admin 27 | INFLUXDB_ADMIN_PASSWORD: st_admin_pw 28 | env: 29 | INFLUXDBCXX_SYSTEMTEST_HOST: influxdb 30 | INFLUXDBCXX_SYSTEMTEST_USER: st_admin 31 | INFLUXDBCXX_SYSTEMTEST_PASSWORD: st_admin_pw 32 | name: "influxdb-${{ matrix.influx_version }} (auth tests: ${{ matrix.auth_enabled }})" 33 | steps: 34 | - uses: actions/checkout@main 35 | - name: Cache Conan Packages 36 | uses: actions/cache@main 37 | with: 38 | path: ~/.conan2/p/ 39 | key: conan-${{ runner.os }}-systemtest-db${{ matrix.influx_version }}-${{ hashFiles('conanfile.py') }} 40 | - name: Setup 41 | run: | 42 | script/ci_setup.sh 43 | add-apt-repository ppa:mhier/libboost-latest 44 | apt-get update 45 | apt-get install -y boost1.83 46 | - name: Build 47 | run: | 48 | cmake --preset conan-release 49 | cmake --build build --target ${{ matrix.auth_enabled == true && 'systemtest-auth' || 'systemtest' }} 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | *~ 3 | /cmake-build-debug 4 | /cmake-build-release 5 | /.idea 6 | CMakeUserPresets.json 7 | test_package/build/ 8 | -------------------------------------------------------------------------------- /3rd-party/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(date INTERFACE) 2 | target_include_directories(date INTERFACE "${CMAKE_CURRENT_LIST_DIR}") 3 | add_library(date::date ALIAS date) 4 | -------------------------------------------------------------------------------- /3rd-party/date/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015, 2016, 2017 Howard Hinnant 4 | Copyright (c) 2016 Adrian Colomitchi 5 | Copyright (c) 2017 Florian Dang 6 | Copyright (c) 2017 Paul Thompson 7 | Copyright (c) 2018, 2019 Tomasz Kamiński 8 | Copyright (c) 2019 Jiangang Zhuang 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | -------------------------------------------------------------------------------- /3rd-party/date/README.md: -------------------------------------------------------------------------------- 1 | # date 2 | 3 | A date and time library based on the C++11/14/17 header 4 | 5 | Visit the [date project](https://github.com/HowardHinnant/date) for further information. 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #################################### 2 | # General project definition 3 | #################################### 4 | 5 | cmake_minimum_required(VERSION 3.12.0) 6 | 7 | # disable testsuite when included 8 | # using add_subdirectory 9 | if(DEFINED PROJECT_NAME) 10 | set(INCLUDED_AS_SUBPROJECT ON) 11 | set(INFLUXCXX_TESTING OFF CACHE BOOL "testing not available in sub-project") 12 | set(INFLUXCXX_SYSTEMTEST OFF CACHE BOOL "system testing not available in sub-project") 13 | set(INFLUXCXX_COVERAGE OFF CACHE BOOL "coverage not available in sub-project") 14 | endif() 15 | 16 | option(BUILD_SHARED_LIBS "Build shared versions of libraries" ON) 17 | option(INFLUXCXX_WITH_BOOST "Build with Boost support enabled" ON) 18 | option(INFLUXCXX_TESTING "Enable testing for this component" ON) 19 | option(INFLUXCXX_SYSTEMTEST "Enable system tests" ON) 20 | option(INFLUXCXX_COVERAGE "Enable Coverage" OFF) 21 | option(INFLUXCXX_WERROR "Build with -Werror enabled (if supported)" ON) 22 | 23 | # Define project 24 | project(influxdb-cxx 25 | VERSION 0.8.0 26 | DESCRIPTION "InfluxDB C++ client library" 27 | LANGUAGES CXX 28 | ) 29 | 30 | message(STATUS "~~~ ${PROJECT_NAME} v${PROJECT_VERSION} ~~~") 31 | 32 | 33 | # Add compiler flags for warnings 34 | if(NOT MSVC AND NOT INCLUDED_AS_SUBPROJECT) 35 | add_compile_options(-Wall 36 | -Wextra 37 | -pedantic 38 | -pedantic-errors 39 | $<$:-Werror> 40 | -Wshadow 41 | -Wold-style-cast 42 | -Wnull-dereference 43 | -Wnon-virtual-dtor 44 | -Woverloaded-virtual 45 | ) 46 | endif() 47 | 48 | set(CMAKE_CXX_STANDARD 20) 49 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 50 | set(CMAKE_CXX_EXTENSIONS OFF) 51 | # We explicitly export the public interface 52 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 53 | set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) 54 | include(GenerateExportHeader) 55 | 56 | # Set fPIC for all targets 57 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 58 | 59 | # Set the default build type to "RelWithDebInfo" 60 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 61 | set(CMAKE_BUILD_TYPE "RelWithDebInfo" 62 | CACHE 63 | STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." 64 | FORCE 65 | ) 66 | endif() 67 | 68 | message(STATUS "Build Type : ${CMAKE_BUILD_TYPE}") 69 | message(STATUS "Boost support : ${INFLUXCXX_WITH_BOOST}") 70 | message(STATUS "Unit Tests : ${INFLUXCXX_TESTING}") 71 | message(STATUS "System Tests : ${INFLUXCXX_SYSTEMTEST}") 72 | message(STATUS "Werror : ${INFLUXCXX_WERROR}") 73 | 74 | 75 | # Add coverage flags 76 | if(INFLUXCXX_COVERAGE) 77 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") 78 | endif() 79 | 80 | #################################### 81 | # Dependencies 82 | #################################### 83 | 84 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") 85 | 86 | find_package(Threads REQUIRED) 87 | find_package(cpr REQUIRED) 88 | 89 | if (INFLUXCXX_WITH_BOOST) 90 | # Fixes warning when using boost from brew 91 | set(Boost_USE_MULTITHREADED TRUE) 92 | 93 | find_package(Boost REQUIRED COMPONENTS system) 94 | endif() 95 | 96 | add_subdirectory(3rd-party) 97 | 98 | #################################### 99 | # Library 100 | #################################### 101 | 102 | # Create library 103 | # note: BUILD_SHARED_LIBS specifies if static or shared 104 | # as boost is build without -fPIC, we cannot 105 | # statically link against it when building 106 | # influxdb as shared object 107 | add_subdirectory("src") 108 | 109 | 110 | #################################### 111 | # Tests 112 | #################################### 113 | 114 | if (INFLUXCXX_TESTING) 115 | enable_testing() 116 | add_subdirectory("test") 117 | endif() 118 | 119 | 120 | #################################### 121 | # Install 122 | #################################### 123 | 124 | include(GNUInstallDirs) 125 | 126 | # Build targets with install rpath on Mac to dramatically speed up installation 127 | # https://gitlab.kitware.com/cmake/community/wikis/doc/cmake/RPATH-handling 128 | set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 129 | list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" isSystemDir) 130 | if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") 131 | if("${isSystemDir}" STREQUAL "-1") 132 | set(CMAKE_INSTALL_RPATH "@loader_path/../${CMAKE_INSTALL_LIBDIR}") 133 | endif() 134 | set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) 135 | endif() 136 | unset(isSystemDir) 137 | 138 | # Install library 139 | install(TARGETS InfluxDB 140 | EXPORT InfluxDBTargets 141 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 142 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 143 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 144 | ) 145 | 146 | # Create version file 147 | include(CMakePackageConfigHelpers) 148 | write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/cmake/InfluxDBConfigVersion.cmake" 149 | VERSION ${PACKAGE_VERSION} 150 | COMPATIBILITY AnyNewerVersion 151 | ) 152 | 153 | # Install headers 154 | install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") 155 | install(FILES ${PROJECT_BINARY_DIR}/src/InfluxDB/influxdb_export.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/InfluxDB") 156 | 157 | # Export targets 158 | install(EXPORT InfluxDBTargets 159 | FILE 160 | InfluxDBTargets.cmake 161 | NAMESPACE 162 | InfluxData:: 163 | DESTINATION 164 | ${CMAKE_INSTALL_LIBDIR}/cmake/InfluxDB 165 | ) 166 | 167 | # Configure and install Config files 168 | configure_package_config_file( 169 | cmake/InfluxDBConfig.cmake.in cmake/InfluxDBConfig.cmake 170 | INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/InfluxDB" 171 | PATH_VARS CMAKE_INSTALL_PREFIX 172 | ) 173 | 174 | install( 175 | FILES 176 | "${CMAKE_CURRENT_BINARY_DIR}/cmake/InfluxDBConfig.cmake" 177 | "${CMAKE_CURRENT_BINARY_DIR}/cmake/InfluxDBConfigVersion.cmake" 178 | DESTINATION 179 | ${CMAKE_INSTALL_LIBDIR}/cmake/InfluxDB 180 | ) 181 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | 4 | ## Issues 5 | 6 | **Issues** are used to report bugs, problems, feature requests, ask questions or other kind of suggestions. An issue ***should include***: 7 | 8 | - Good and meaningful title 9 | - Detailed description 10 | 11 | ### Problems and Bugs 12 | 13 | Problem and bug reports ***need also***: 14 | 15 | - Expected and actual behaviour 16 | - Used version, compiler and platform 17 | - Minimal example or steps to reproduce 18 | 19 | 20 | ## Pull Requests 21 | 22 | **Pull requests** are used to submit contributions to the project. A pull request ***should include***: 23 | 24 | - Good and meaningful title 25 | - Detailed description 26 | - Descriptive commit messages 27 | 28 | If the PR is related to an *Issue*, please reference it in the description or title. 29 | 30 | ### Code 31 | 32 | The contributed code should match these criteria: 33 | 34 | - Pass all Unit Tests and CI Builds 35 | - Proper Test Cases 36 | - Merge cleanly, without conflicts 37 | - Follow the projects code style 38 | - Does not introduce external dependencies 39 | - 4 Spaces – no Tabs 40 | - UTF-8 encoding 41 | 42 | 43 | 44 | ## Further readings 45 | 46 | - [Github Guide: How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) 47 | 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2025 offa 4 | Copyright (c) 2019 Adam Wegrzynek 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # influxdb-cxx 2 | 3 | [![ci](https://github.com/offa/influxdb-cxx/actions/workflows/ci.yml/badge.svg)](https://github.com/offa/influxdb-cxx/actions/workflows/ci.yml) 4 | [![GitHub release](https://img.shields.io/github/release/offa/influxdb-cxx.svg)](https://github.com/offa/influxdb-cxx/releases) 5 | [![License](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE) 6 | ![C++](https://img.shields.io/badge/c++-20-green.svg) 7 | [![Conan Center](https://img.shields.io/conan/v/influxdb-cxx)](https://conan.io/center/recipes/influxdb-cxx) 8 | 9 | InfluxDB C++ client library 10 | - Batch write 11 | - Data exploration 12 | - Supported transports 13 | - HTTP/HTTPS 14 | - UDP 15 | - Unix datagram socket 16 | - TCP 17 | 18 | *Fork of the unmaintained [awegrzyn/influxdb-cxx](https://github.com/awegrzyn/influxdb-cxx) project.* 19 | 20 | ## Installation 21 | 22 | ### Build requirements 23 | - CMake 3.12+ 24 | - C++20 compiler 25 | 26 | ### Dependencies 27 | - [**cpr**](https://github.com/libcpr/cpr) (required)i) 28 | - **boost 1.78+** (optional – see [Transports](#transports)) 29 | 30 | i) *cpr* needs to provide [CMake support](https://github.com/libcpr/cpr#find_package); some systems need to call `ldconfig` after *.so* installation. 31 | 32 | 33 | ### Generic 34 | ```bash 35 | mkdir build && cd build 36 | cmake .. 37 | sudo make install 38 | ``` 39 | 40 | ## Quick start 41 | 42 | ### Include in CMake project 43 | 44 | The InfluxDB library is exported as target `InfluxData::InfluxDB`. 45 | 46 | ```cmake 47 | project(example) 48 | 49 | find_package(InfluxDB) 50 | 51 | add_executable(example-influx main.cpp) 52 | target_link_libraries(example-influx PRIVATE InfluxData::InfluxDB) 53 | ``` 54 | 55 | This target is also provided when the project is included as a subdirectory. 56 | 57 | ```cmake 58 | project(example) 59 | add_subdirectory(influxdb-cxx) 60 | add_executable(example-influx main.cpp) 61 | target_link_libraries(example-influx PRIVATE InfluxData::InfluxDB) 62 | ``` 63 | 64 | ### Basic write 65 | 66 | ```cpp 67 | // Provide complete URI 68 | auto influxdb = influxdb::InfluxDBFactory::Get("http://localhost:8086?db=test"); 69 | influxdb->write(influxdb::Point{"test"} 70 | .addField("value", 10) 71 | .addTag("host", "localhost") 72 | ); 73 | ``` 74 | 75 | ### Batch write 76 | 77 | ```cpp 78 | auto influxdb = influxdb::InfluxDBFactory::Get("http://localhost:8086?db=test"); 79 | // Write batches of 100 points 80 | influxdb->batchOf(100); 81 | 82 | for (;;) { 83 | influxdb->write(influxdb::Point{"test"}.addField("value", 10)); 84 | } 85 | ``` 86 | 87 | ###### Note: 88 | 89 | When batch write is enabled, call `flushBatch()` to flush pending batches. 90 | This is of particular importance to ensure all points are written prior to destruction. 91 | 92 | ```cpp 93 | auto influxdb = influxdb::InfluxDBFactory::Get("http://localhost:8086?db=test"); 94 | influxdb->batchOf(3); 95 | influxdb->write(influxdb::Point{"test"}.addField("value", 1)); 96 | influxdb->write(influxdb::Point{"test"}.addField("value", 2)); 97 | 98 | // Flush batches, both points are written 99 | influxdb->flushBatch(); 100 | ``` 101 | 102 | 103 | ### Query 104 | 105 | ```cpp 106 | // Available over HTTP only 107 | auto influxdb = influxdb::InfluxDBFactory::Get("http://localhost:8086?db=test"); 108 | /// Pass an IFQL to get list of points 109 | std::vector points = influxdb->query("SELECT * FROM test"); 110 | ``` 111 | 112 | ### Execute cmd 113 | 114 | ```cpp 115 | auto influxdb = influxdb::InfluxDBFactory::Get("http://localhost:8086?db=test"); 116 | 117 | // Execute a command and receive it's response 118 | const auto response = influxdb->execute("SHOW DATABASES"); 119 | ``` 120 | 121 | ## Transports 122 | 123 | Supported transports: 124 | 125 | | Name | Dependency | URI protocol | Sample URI | 126 | | ----------- |:-----------:|:--------------:| -------------------------------------:| 127 | | HTTP | cpri) | `http`/`https` | `http://localhost:8086?db=` | 128 | | TCP | boost | `tcp` | `tcp://localhost:8094` | 129 | | UDP | boost | `udp` | `udp://localhost:8094` | 130 | | Unix socket | boost | `unix` | `unix:///tmp/telegraf.sock` | 131 | 132 | i) boost is needed to support queries. 133 | 134 | ### Configuration by URI 135 | 136 | An underlying transport is configurable by passing an URI. `[protocol]` determines the actual transport type: 137 | 138 | ```cpp 139 | auto influxdb = influxdb::InfluxDBFactory::Get("http://localhost:8086?db=test"); 140 | ``` 141 | 142 | URI Format: 143 | 144 | ``` 145 | [protocol]://[username:password@]host:port[?db=database] 146 | 147 | # Auth token: 148 | [protocol]://[token@]host:port[?db=database] 149 | ``` 150 | 151 | ### Additional transport configuration *(HTTP only)* 152 | 153 | The HTTP transport supports additional configurations beyond the limited URI parameters: 154 | 155 | ```cpp 156 | auto influxdb = InfluxDBBuilder::http("http://localhost:8086?db=test") 157 | .setTimeout(std::chrono::seconds{20}) 158 | .setAuthToken("") 159 | .connect(); 160 | ``` 161 | 162 | 163 | ## InfluxDB v2.x compatibility 164 | 165 | The support for InfluxDB v2.x is limited at the moment. It's possible to use the v1.x compatibility backend though. 166 | 167 | Please visit [*InfluxDB 1.x compatibility API* docs](https://docs.influxdata.com/influxdb/v2.6/reference/api/influxdb-1x/) for more information. 168 | 169 | To create a v1.x compatible user (as described [here](https://docs.influxdata.com/influxdb/v2.6/reference/cli/influx/v1/auth/)): 170 | 171 | ```sh 172 | influx v1 auth create --read-bucket ${BUCKET_ID} --write-bucket ${BUCKET_ID} --username ${USERNAME} --password ${PASSWORD} 173 | ``` 174 | 175 | 176 | ## Thread safety 177 | 178 | This library is not thread-safe. Using it in a multi-threaded environment without proper synchronization mechanisms may lead to unexpected behavior and data corruption. 179 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | To report a security issue, please use [*Report a Vulnerability*](https://github.com/offa/influxdb-cxx/security/advisories/new). **Do not** submit a public issue. 6 | 7 | ## Responsible Disclosure 8 | 9 | Security vulnerabilities are taken seriously, and help in disclosing them responsibly is appreciated. Please refrain from disclosing the vulnerability publicly until it has been addressed. 10 | -------------------------------------------------------------------------------- /cmake/InfluxDBConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | set(InfluxDB_VERSION @PROJECT_VERSION@) 4 | set(InfluxDB_WITH_BOOST @INFLUXCXX_WITH_BOOST@) 5 | 6 | get_filename_component(InfluxDB_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) 7 | include(CMakeFindDependencyMacro) 8 | 9 | if(InfluxDB_WITH_BOOST) 10 | find_dependency(Boost COMPONENTS system REQUIRED) 11 | endif() 12 | find_dependency(cpr REQUIRED) 13 | find_dependency(Threads REQUIRED) 14 | 15 | if(NOT TARGET InfluxData::InfluxDB) 16 | include("${InfluxDB_CMAKE_DIR}/InfluxDBTargets.cmake") 17 | endif() 18 | 19 | message(STATUS "InfluxDB ${InfluxDB_VERSION} found") 20 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | from conan import ConanFile 2 | 3 | class InfluxdbCxxConan(ConanFile): 4 | settings = "os", "compiler", "build_type", "arch" 5 | generators = "CMakeDeps", "CMakeToolchain" 6 | 7 | options = { 8 | "tests": [True, False], 9 | "system": [True, False], 10 | "boost": [True, False] 11 | } 12 | default_options = { 13 | "tests": True, 14 | "system": False, 15 | "boost": True, 16 | "boost/*:shared": True, 17 | } 18 | 19 | def requirements(self): 20 | self.requires("cpr/1.11.2") 21 | if not self.options.system and self.options.boost: 22 | self.requires("boost/1.85.0") 23 | if self.options.tests: 24 | self.requires("catch2/3.8.1") 25 | self.requires("trompeloeil/49") 26 | -------------------------------------------------------------------------------- /include/InfluxDB/InfluxDB.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // Copyright (c) 2019 Adam Wegrzynek 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | /// 25 | /// \author Adam Wegrzynek 26 | /// 27 | 28 | #ifndef INFLUXDATA_INFLUXDB_H 29 | #define INFLUXDATA_INFLUXDB_H 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "InfluxDB/Transport.h" 38 | #include "InfluxDB/Point.h" 39 | #include "InfluxDB/influxdb_export.h" 40 | 41 | namespace influxdb 42 | { 43 | 44 | class INFLUXDB_EXPORT InfluxDB 45 | { 46 | public: 47 | /// Disable copy constructor 48 | InfluxDB& operator=(const InfluxDB&) = delete; 49 | 50 | /// Disable copy constructor 51 | InfluxDB(const InfluxDB&) = delete; 52 | 53 | /// Constructor required valid transport 54 | explicit InfluxDB(std::unique_ptr transport); 55 | 56 | /// Writes a point 57 | /// \param point 58 | void write(Point&& point); 59 | 60 | /// Writes a vector of point 61 | /// \param point 62 | void write(std::vector&& points); 63 | 64 | /// Queries InfluxDB database 65 | std::vector query(const std::string& query); 66 | 67 | /// Create InfluxDB database if does not exists 68 | void createDatabaseIfNotExists(); 69 | 70 | /// Flushes points batched (this can also happens when buffer is full) 71 | void flushBatch(); 72 | 73 | /// Enables points batching 74 | /// \param size 75 | void batchOf(std::size_t size = 32); 76 | 77 | /// Returns current batch size 78 | std::size_t batchSize() const; 79 | 80 | /// Clears the point batch 81 | void clearBatch(); 82 | 83 | /// Adds a global tag 84 | /// \param name 85 | /// \param value 86 | void addGlobalTag(std::string_view name, std::string_view value); 87 | 88 | /// Executes a command and returns it's response. 89 | /// \param cmd 90 | std::string execute(const std::string& cmd); 91 | 92 | private: 93 | void addPointToBatch(Point&& point); 94 | 95 | /// line protocol batch to be written 96 | std::deque mPointBatch; 97 | 98 | /// Flag stating whether point buffering is enabled 99 | bool mIsBatchingActivated; 100 | 101 | /// Points batch size 102 | std::size_t mBatchSize; 103 | 104 | /// Underlying transport UDP/HTTP/Unix socket 105 | std::unique_ptr mTransport; 106 | 107 | /// Transmits string over transport 108 | void transmit(std::string&& point); 109 | 110 | /// List of global tags 111 | std::string mGlobalTags; 112 | 113 | std::string joinLineProtocolBatch() const; 114 | }; 115 | 116 | } // namespace influxdb 117 | 118 | #endif // INFLUXDATA_INFLUXDB_H 119 | -------------------------------------------------------------------------------- /include/InfluxDB/InfluxDBBuilder.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #ifndef INFLUXDATA_INFLUXDBBUILDER_H 24 | #define INFLUXDATA_INFLUXDBBUILDER_H 25 | 26 | #include "InfluxDB/InfluxDB.h" 27 | #include "InfluxDB/Transport.h" 28 | #include "InfluxDB/Proxy.h" 29 | #include "InfluxDB/influxdb_export.h" 30 | #include 31 | 32 | namespace influxdb 33 | { 34 | class INFLUXDB_EXPORT InfluxDBBuilder 35 | { 36 | public: 37 | std::unique_ptr connect(); 38 | 39 | InfluxDBBuilder&& setBasicAuthentication(const std::string& user, const std::string& pass); 40 | InfluxDBBuilder&& setAuthToken(const std::string& token); 41 | InfluxDBBuilder&& setProxy(const Proxy& proxy); 42 | InfluxDBBuilder&& setTimeout(std::chrono::milliseconds timeout); 43 | InfluxDBBuilder&& setVerifyCertificate(bool verify); 44 | 45 | static InfluxDBBuilder http(const std::string& url); 46 | 47 | private: 48 | explicit InfluxDBBuilder(std::unique_ptr impl); 49 | 50 | std::unique_ptr transport; 51 | }; 52 | } 53 | 54 | #endif // INFLUXDATA_INFLUXDBBUILDER_H 55 | -------------------------------------------------------------------------------- /include/InfluxDB/InfluxDBException.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // Copyright (c) 2019 Adam Wegrzynek 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | /// 25 | /// \author Adam Wegrzynek 26 | /// 27 | 28 | #ifndef INFLUXDATA_EXCEPTION_H 29 | #define INFLUXDATA_EXCEPTION_H 30 | 31 | #include 32 | #include 33 | 34 | #include "InfluxDB/influxdb_export.h" 35 | 36 | namespace influxdb 37 | { 38 | 39 | class INFLUXDB_EXPORT InfluxDBException : public std::runtime_error 40 | { 41 | public: 42 | explicit InfluxDBException(const std::string& message) 43 | : std::runtime_error::runtime_error(message) 44 | { 45 | } 46 | }; 47 | 48 | } // namespace influxdb 49 | 50 | #endif // INFLUXDATA_EXCEPTION_H 51 | -------------------------------------------------------------------------------- /include/InfluxDB/InfluxDBFactory.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // Copyright (c) 2019 Adam Wegrzynek 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | /// 25 | /// \author Adam Wegrzynek 26 | /// 27 | 28 | #ifndef INFLUXDATA_INFLUXDB_FACTORY_H 29 | #define INFLUXDATA_INFLUXDB_FACTORY_H 30 | 31 | #include "InfluxDB/InfluxDB.h" 32 | #include "InfluxDB/Transport.h" 33 | #include "InfluxDB/influxdb_export.h" 34 | 35 | namespace influxdb 36 | { 37 | 38 | /// \brief InfluxDB factory 39 | class INFLUXDB_EXPORT InfluxDBFactory 40 | { 41 | public: 42 | /// Disables copy constructor 43 | InfluxDBFactory& operator=(const InfluxDBFactory&) = delete; 44 | 45 | /// Disables copy constructor 46 | InfluxDBFactory(const InfluxDBFactory&) = delete; 47 | 48 | /// InfluxDB factory 49 | /// Provides InfluxDB instance with given transport 50 | /// \param url URL defining transport details 51 | /// \throw InfluxDBException if unrecognised backend or missing protocol 52 | static std::unique_ptr Get(const std::string& url) noexcept(false); 53 | 54 | /// InfluxDB factory 55 | /// Provides InfluxDB instance with given transport and proxy 56 | /// \param url URL defining transport details 57 | /// \param proxy Proxy 58 | /// \throw InfluxDBException if unrecognised backend, missing protocol or unsupported proxy 59 | static std::unique_ptr Get(const std::string& url, const Proxy& proxy); 60 | 61 | private: 62 | ///\return backend based on provided URL 63 | static std::unique_ptr GetTransport(const std::string& url); 64 | 65 | /// Private constructor disallows to create instance of Factory 66 | InfluxDBFactory() = default; 67 | }; 68 | 69 | } // namespace influxdb 70 | 71 | #endif // INFLUXDATA_INFLUXDB_FACTORY_H 72 | -------------------------------------------------------------------------------- /include/InfluxDB/Point.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // Copyright (c) 2019 Adam Wegrzynek 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | /// 25 | /// \author Adam Wegrzynek 26 | /// 27 | 28 | #ifndef INFLUXDATA_POINT_H 29 | #define INFLUXDATA_POINT_H 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "InfluxDB/influxdb_export.h" 38 | 39 | namespace influxdb 40 | { 41 | 42 | static inline constexpr int defaultFloatsPrecision{18}; 43 | 44 | /// \brief Represents a point 45 | class INFLUXDB_EXPORT Point 46 | { 47 | public: 48 | /// Constructs point based on measurement name 49 | explicit Point(const std::string& measurement); 50 | 51 | /// Adds a tags 52 | Point&& addTag(std::string_view key, std::string_view value); 53 | 54 | /// Adds field 55 | using FieldValue = std::variant; 56 | Point&& addField(std::string_view name, const FieldValue& value); 57 | 58 | /// Sets custom timestamp 59 | Point&& setTimestamp(std::chrono::time_point timestamp); 60 | 61 | /// Name getter 62 | std::string getName() const; 63 | 64 | /// Timestamp getter 65 | std::chrono::time_point getTimestamp() const; 66 | 67 | /// Fields getter 68 | std::string getFields() const; 69 | 70 | /// Get Field Set 71 | using FieldSet = std::deque>; 72 | const FieldSet& getFieldSet() const; 73 | 74 | /// Tags getter 75 | std::string getTags() const; 76 | 77 | /// Get Tag Set 78 | using TagSet = std::deque>; 79 | const TagSet& getTagSet() const; 80 | 81 | /// Precision for float fields 82 | static inline int floatsPrecision{defaultFloatsPrecision}; 83 | 84 | protected: 85 | /// A name 86 | std::string mMeasurement; 87 | 88 | /// A timestamp 89 | std::chrono::time_point mTimestamp; 90 | 91 | //// Tags 92 | TagSet mTags; 93 | 94 | //// Fields 95 | FieldSet mFields; 96 | }; 97 | 98 | } // namespace influxdb 99 | 100 | #endif // INFLUXDATA_POINT_H 101 | -------------------------------------------------------------------------------- /include/InfluxDB/Proxy.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #ifndef INFLUXDATA_PROXY_H 24 | #define INFLUXDATA_PROXY_H 25 | 26 | #include "InfluxDB/influxdb_export.h" 27 | #include 28 | #include 29 | 30 | namespace influxdb 31 | { 32 | class INFLUXDB_EXPORT Proxy 33 | { 34 | public: 35 | struct Auth 36 | { 37 | std::string user; 38 | std::string password; 39 | }; 40 | 41 | 42 | Proxy(const std::string& proxy, Auth auth); 43 | explicit Proxy(const std::string& proxy); 44 | 45 | const std::string& getProxy() const; 46 | std::optional getAuthentication() const; 47 | 48 | private: 49 | std::string proxy_; 50 | std::optional auth_; 51 | }; 52 | } 53 | #endif /* INFLUXDATA_PROXY_H */ 54 | -------------------------------------------------------------------------------- /include/InfluxDB/Transport.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // Copyright (c) 2019 Adam Wegrzynek 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | /// 25 | /// \author Adam Wegrzynek 26 | /// 27 | 28 | #ifndef INFLUXDATA_TRANSPORTINTERFACE_H 29 | #define INFLUXDATA_TRANSPORTINTERFACE_H 30 | 31 | #include "InfluxDB/InfluxDBException.h" 32 | #include "InfluxDB/influxdb_export.h" 33 | #include "InfluxDB/Proxy.h" 34 | 35 | namespace influxdb 36 | { 37 | 38 | /// \brief Transport interface 39 | class INFLUXDB_EXPORT Transport 40 | { 41 | public: 42 | Transport() = default; 43 | 44 | virtual ~Transport() = default; 45 | 46 | /// Sends string blob 47 | virtual void send(std::string&& message) = 0; 48 | 49 | /// Sends request 50 | virtual std::string query([[maybe_unused]] const std::string& query) 51 | { 52 | throw InfluxDBException{"Queries are not supported by the selected transport"}; 53 | } 54 | 55 | /// Executes command 56 | virtual std::string execute([[maybe_unused]] const std::string& cmd) 57 | { 58 | throw InfluxDBException{"Execution is not supported by the selected transport"}; 59 | } 60 | 61 | /// Sends request 62 | virtual void createDatabase() 63 | { 64 | throw InfluxDBException{"Creation of database is not supported by the selected transport"}; 65 | } 66 | 67 | /// Sets proxy 68 | virtual void setProxy([[maybe_unused]] const Proxy& proxy) 69 | { 70 | throw InfluxDBException{"Proxy is not supported by the selected transport"}; 71 | } 72 | }; 73 | 74 | } // namespace influxdb 75 | 76 | #endif // INFLUXDATA_TRANSPORTINTERFACE_H 77 | -------------------------------------------------------------------------------- /script/ci_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | mkdir -p build && cd build 6 | 7 | cmake --preset conan-release "$@" .. 8 | cmake --build . -j 9 | cmake --build . --target unittest 10 | -------------------------------------------------------------------------------- /script/ci_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | export DEBIAN_FRONTEND=noninteractive 6 | export PATH=$HOME/.local/bin:$PATH 7 | apt-get update 8 | apt-get install -y pipx 9 | pipx install conan 10 | conan profile detect 11 | 12 | mkdir -p build && cd build 13 | 14 | if [[ "${CXX}" == clang* ]] 15 | then 16 | STDLIB_ENV="CXXFLAGS=\"-stdlib=libc++\"" 17 | export ${STDLIB_ENV} 18 | echo "${STDLIB_ENV}" >> ${GITHUB_ENV} 19 | sed -i 's/^compiler.libcxx=.*$/compiler.libcxx=libc++/g' ~/.conan2/profiles/default 20 | fi 21 | 22 | conan install \ 23 | -of . \ 24 | -o "&:system=True" \ 25 | -o "&:tests=True" \ 26 | -s compiler.cppstd=20 \ 27 | --build=missing \ 28 | .. 29 | -------------------------------------------------------------------------------- /script/ci_testdeploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # test including influxdb-cxx 3 | 4 | set -ex 5 | PID=$$ 6 | BASEPATH=/tmp/influx-test-${PID} 7 | BUILD_TYPE="Release" 8 | export PATH=$HOME/.local/bin:$PATH 9 | 10 | echo "perform deployment test in ${BASEPATH}" 11 | mkdir -p ${BASEPATH}/influxdb-cxx 12 | cp -r ./* ${BASEPATH}/influxdb-cxx 13 | cp -r script/include_library/* ${BASEPATH}/ 14 | 15 | cd /tmp/influx-test-${PID}/ 16 | mkdir build && cd build 17 | 18 | conan install \ 19 | -of . \ 20 | -g CMakeToolchain \ 21 | -g CMakeDeps \ 22 | --build=missing \ 23 | -s build_type=${BUILD_TYPE} \ 24 | -s compiler.cppstd=20 \ 25 | --requires=cpr/1.11.2 26 | 27 | cmake -DCMAKE_TOOLCHAIN_FILE=./conan_toolchain.cmake -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" "$@" .. 28 | cmake --build . -j 29 | 30 | #cleanup 31 | rm -r ${BASEPATH} 32 | -------------------------------------------------------------------------------- /script/include_library/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(influxcxx-test) 3 | 4 | if(INFLUXCXX_AS_SUBDIR) 5 | message(STATUS "include as subdirectory") 6 | add_subdirectory(influxdb-cxx) 7 | else() 8 | message(STATUS "include using find_package") 9 | find_package(InfluxDB REQUIRED) 10 | endif() 11 | 12 | add_executable(influxcxx-writer main.cxx) 13 | target_link_libraries(influxcxx-writer PRIVATE InfluxData::InfluxDB) 14 | -------------------------------------------------------------------------------- /script/include_library/main.cxx: -------------------------------------------------------------------------------- 1 | // Sample project to check if the deployment process works 2 | #include 3 | 4 | int main() 5 | { 6 | auto influxdb = influxdb::InfluxDBFactory::Get("http://localhost:8086?db=test"); 7 | influxdb->write(influxdb::Point{"test"}.addField("value", 10)); 8 | } 9 | -------------------------------------------------------------------------------- /src/BoostSupport.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // Copyright (c) 2019 Adam Wegrzynek 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | #include "BoostSupport.h" 25 | #include "UDP.h" 26 | #include "TCP.h" 27 | #include "UnixSocket.h" 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | namespace influxdb::internal 35 | { 36 | namespace 37 | { 38 | std::chrono::system_clock::time_point parseTimeStamp(const std::string& value) 39 | { 40 | std::istringstream timeString{value}; 41 | std::chrono::system_clock::time_point timeStamp; 42 | timeString >> date::parse("%FT%T%Z", timeStamp); 43 | return timeStamp; 44 | } 45 | } 46 | 47 | std::vector queryImpl(Transport* transport, const std::string& query) 48 | { 49 | const auto response = transport->query(query); 50 | std::stringstream responseString; 51 | responseString << response; 52 | std::vector points; 53 | boost::property_tree::ptree pt; 54 | boost::property_tree::read_json(responseString, pt); 55 | 56 | for (const auto& result : pt.get_child("results")) 57 | { 58 | if (const auto isResultEmpty = result.second.find("series"); isResultEmpty == result.second.not_found()) 59 | { 60 | return {}; 61 | } 62 | for (const auto& series : result.second.get_child("series")) 63 | { 64 | for (const auto& values : series.second.get_child("values")) 65 | { 66 | Point point{series.second.get("name", "")}; 67 | 68 | if (const auto tags = series.second.get_child_optional("tags"); tags) 69 | { 70 | for (const auto& tag : tags.get()) 71 | { 72 | point.addTag(tag.first, tag.second.data()); 73 | } 74 | } 75 | const auto columns = series.second.get_child("columns"); 76 | auto iColumns = columns.begin(); 77 | auto iValues = values.second.begin(); 78 | for (; iColumns != columns.end() && iValues != values.second.end(); ++iColumns, ++iValues) 79 | { 80 | const auto value = iValues->second.get_value(); 81 | const auto column = iColumns->second.get_value(); 82 | if (column == "time") 83 | { 84 | point.setTimestamp(parseTimeStamp(value)); 85 | continue; 86 | } 87 | // cast all values to double, if strings add to tags 88 | try 89 | { 90 | point.addField(column, boost::lexical_cast(value)); 91 | } 92 | catch (...) 93 | { 94 | point.addTag(column, value); 95 | } 96 | } 97 | points.push_back(std::move(point)); 98 | } 99 | } 100 | } 101 | return points; 102 | } 103 | 104 | std::unique_ptr withUdpTransport(const http::url& uri) 105 | { 106 | return std::make_unique(uri.host, uri.port); 107 | } 108 | 109 | std::unique_ptr withTcpTransport(const http::url& uri) 110 | { 111 | return std::make_unique(uri.host, uri.port); 112 | } 113 | 114 | std::unique_ptr withUnixSocketTransport(const http::url& uri) 115 | { 116 | return std::make_unique(uri.path); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/BoostSupport.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // Copyright (c) 2019 Adam Wegrzynek 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | #pragma once 25 | 26 | #include "InfluxDB/Transport.h" 27 | #include "InfluxDB/Point.h" 28 | #include "UriParser.h" 29 | #include 30 | #include 31 | #include 32 | 33 | namespace influxdb::internal 34 | { 35 | std::vector queryImpl(Transport* transport, const std::string& query); 36 | 37 | std::unique_ptr withUdpTransport(const http::url& uri); 38 | std::unique_ptr withTcpTransport(const http::url& uri); 39 | std::unique_ptr withUnixSocketTransport(const http::url& uri); 40 | } 41 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(INTERNAL_INCLUDE_DIRS 2 | ${CMAKE_CURRENT_SOURCE_DIR} 3 | ${PROJECT_SOURCE_DIR}/include 4 | ${PROJECT_BINARY_DIR}/src 5 | ) 6 | 7 | 8 | add_library(InfluxDB-BoostSupport OBJECT 9 | $<$>:NoBoostSupport.cxx> 10 | $<$:BoostSupport.cxx UDP.cxx TCP.cxx UnixSocket.cxx> 11 | ) 12 | target_include_directories(InfluxDB-BoostSupport PRIVATE ${INTERNAL_INCLUDE_DIRS}) 13 | 14 | target_link_libraries(InfluxDB-BoostSupport PRIVATE date::date) 15 | 16 | if (INFLUXCXX_WITH_BOOST) 17 | target_link_libraries(InfluxDB-BoostSupport PRIVATE Boost::boost Boost::system) 18 | endif() 19 | 20 | # #117: Workaround for Boost ASIO null-dereference 21 | if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "12") 22 | set_source_files_properties(UDP.cxx TCP.cxx UnixSocket.cxx PROPERTIES COMPILE_OPTIONS "-Wno-null-dereference") 23 | endif() 24 | 25 | add_library(InfluxDB-Internal OBJECT LineProtocol.cxx HTTP.cxx) 26 | target_include_directories(InfluxDB-Internal PRIVATE ${INTERNAL_INCLUDE_DIRS}) 27 | target_link_libraries(InfluxDB-Internal PRIVATE cpr::cpr) 28 | 29 | 30 | add_library(InfluxDB-Core OBJECT 31 | InfluxDB.cxx 32 | Point.cxx 33 | InfluxDBFactory.cxx 34 | InfluxDBBuilder.cxx 35 | Proxy.cxx 36 | ) 37 | target_include_directories(InfluxDB-Core PUBLIC 38 | ${PROJECT_SOURCE_DIR}/include 39 | ${PROJECT_BINARY_DIR}/src 40 | ) 41 | target_link_libraries(InfluxDB-Core PRIVATE cpr::cpr) 42 | 43 | 44 | add_library(InfluxDB 45 | $ 46 | $ 47 | $ 48 | ) 49 | add_library(InfluxData::InfluxDB ALIAS InfluxDB) 50 | 51 | # 52 | # Here are a set of rules to help you update your library version information: 53 | # 54 | # If the library source code has changed at all since the last update, 55 | # then increment revision (‘c:r:a’ becomes ‘c:r+1:a’). 56 | # If any interfaces have been added, removed, or changed since the last update, 57 | # increment current, and set revision to 0. 58 | # If any interfaces have been added since the last public release, 59 | # then increment age. 60 | # If any interfaces have been removed or changed since the last public release, 61 | # then set age to 0. 62 | # 63 | # set_target_properties(InfluxDB PROPERTIES VERSION c.r.a SOVERSION c) 64 | # 65 | set(SO_VERSION_MAJOR 1) 66 | set_target_properties(InfluxDB PROPERTIES 67 | VERSION ${SO_VERSION_MAJOR}.0.0 68 | SOVERSION ${SO_VERSION_MAJOR} 69 | ) 70 | 71 | generate_export_header(InfluxDB EXPORT_FILE_NAME "${PROJECT_BINARY_DIR}/src/InfluxDB/influxdb_export.h") 72 | 73 | target_include_directories(InfluxDB 74 | PUBLIC 75 | $ 76 | $ 77 | # for export header 78 | $ 79 | PRIVATE 80 | ${CMAKE_CURRENT_SOURCE_DIR} 81 | ) 82 | 83 | # Link targets 84 | target_link_libraries(InfluxDB 85 | PUBLIC 86 | cpr::cpr 87 | Threads::Threads 88 | ) 89 | 90 | target_compile_features(InfluxDB PUBLIC cxx_std_${CMAKE_CXX_STANDARD}) 91 | -------------------------------------------------------------------------------- /src/HTTP.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // Copyright (c) 2019 Adam Wegrzynek 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | /// 25 | /// \author Adam Wegrzynek 26 | /// 27 | 28 | #include "HTTP.h" 29 | #include "InfluxDB/InfluxDBException.h" 30 | 31 | namespace influxdb::transports 32 | { 33 | namespace 34 | { 35 | void checkResponse(const cpr::Response& resp) 36 | { 37 | if (resp.error) 38 | { 39 | throw InfluxDBException{"Request error: (" + std::to_string(static_cast(resp.error.code)) + ") " + resp.error.message}; 40 | } 41 | if (!cpr::status::is_success(resp.status_code)) 42 | { 43 | throw InfluxDBException{"Request failed: (" + std::to_string(resp.status_code) + ") " + resp.reason + " (message: '" + resp.text + "')"}; 44 | } 45 | } 46 | 47 | std::string parseUrl(const std::string& url) 48 | { 49 | const auto questionMarkPosition = url.find('?'); 50 | 51 | if (questionMarkPosition == std::string::npos) 52 | { 53 | return url; 54 | } 55 | if (url.at(questionMarkPosition - 1) == '/') 56 | { 57 | return url.substr(0, questionMarkPosition - 1); 58 | } 59 | return url.substr(0, questionMarkPosition); 60 | } 61 | 62 | std::string parseDatabaseName(const std::string& url) 63 | { 64 | const auto dbParameterPosition = url.find("?db="); 65 | 66 | if (dbParameterPosition == std::string::npos) 67 | { 68 | throw InfluxDBException{"No Database specified"}; 69 | } 70 | return url.substr(dbParameterPosition + 4); 71 | } 72 | } 73 | 74 | 75 | HTTP::HTTP(const std::string& url) 76 | : endpointUrl(parseUrl(url)), databaseName(parseDatabaseName(url)) 77 | { 78 | session.SetTimeout(cpr::Timeout{std::chrono::seconds{10}}); 79 | session.SetConnectTimeout(cpr::ConnectTimeout{std::chrono::seconds{10}}); 80 | } 81 | 82 | std::string HTTP::query(const std::string& query) 83 | { 84 | session.SetUrl(cpr::Url{endpointUrl + "/query"}); 85 | session.SetParameters(cpr::Parameters{{"db", databaseName}, {"q", query}}); 86 | 87 | const auto response = session.Get(); 88 | checkResponse(response); 89 | 90 | return response.text; 91 | } 92 | 93 | void HTTP::setBasicAuthentication(const std::string& user, const std::string& pass) 94 | { 95 | session.SetAuth(cpr::Authentication{user, pass, cpr::AuthMode::BASIC}); 96 | } 97 | 98 | void HTTP::setAuthToken(const std::string& token) 99 | { 100 | session.UpdateHeader(cpr::Header{{"Authorization", "Token " + token}}); 101 | } 102 | 103 | void HTTP::send(std::string&& lineprotocol) 104 | { 105 | session.SetUrl(cpr::Url{endpointUrl + "/write"}); 106 | session.UpdateHeader(cpr::Header{{"Content-Type", "application/json"}}); 107 | session.SetParameters(cpr::Parameters{{"db", databaseName}}); 108 | session.SetBody(cpr::Body{lineprotocol}); 109 | 110 | const auto response = session.Post(); 111 | checkResponse(response); 112 | } 113 | 114 | void HTTP::setProxy(const Proxy& proxy) 115 | { 116 | session.SetProxies(cpr::Proxies{{"http", proxy.getProxy()}, {"https", proxy.getProxy()}}); 117 | 118 | if (const auto& auth = proxy.getAuthentication(); auth.has_value()) 119 | { 120 | session.SetProxyAuth(cpr::ProxyAuthentication{{"http", cpr::EncodedAuthentication{auth->user, auth->password}}, 121 | {"https", cpr::EncodedAuthentication{auth->user, auth->password}}}); 122 | } 123 | } 124 | 125 | void HTTP::setVerifyCertificate(bool verify) 126 | { 127 | session.SetVerifySsl(verify); 128 | } 129 | 130 | void HTTP::setTimeout(std::chrono::milliseconds timeout) 131 | { 132 | session.SetTimeout(timeout); 133 | session.SetConnectTimeout(timeout); 134 | } 135 | 136 | std::string HTTP::execute(const std::string& cmd) 137 | { 138 | session.SetUrl(cpr::Url{endpointUrl + "/query"}); 139 | session.SetParameters(cpr::Parameters{{"db", databaseName}, {"q", cmd}}); 140 | 141 | const auto response = session.Get(); 142 | checkResponse(response); 143 | 144 | return response.text; 145 | } 146 | 147 | void HTTP::createDatabase() 148 | { 149 | session.SetUrl(cpr::Url{endpointUrl + "/query"}); 150 | session.SetParameters(cpr::Parameters{{"q", "CREATE DATABASE " + databaseName}}); 151 | 152 | const auto response = session.Post(); 153 | checkResponse(response); 154 | } 155 | 156 | } // namespace influxdb 157 | -------------------------------------------------------------------------------- /src/HTTP.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // Copyright (c) 2019 Adam Wegrzynek 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | /// 25 | /// \author Adam Wegrzynek 26 | /// 27 | 28 | #ifndef INFLUXDATA_TRANSPORTS_HTTP_H 29 | #define INFLUXDATA_TRANSPORTS_HTTP_H 30 | 31 | #include "InfluxDB/Transport.h" 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | namespace influxdb::transports 38 | { 39 | 40 | /// \brief HTTP transport 41 | class HTTP : public Transport 42 | { 43 | public: 44 | /// Constructor 45 | explicit HTTP(const std::string& url); 46 | 47 | /// Sends point via HTTP POST 48 | /// \throw InfluxDBException when send fails 49 | void send(std::string&& lineprotocol) override; 50 | 51 | /// Queries database 52 | /// \throw InfluxDBException when query fails 53 | std::string query(const std::string& query) override; 54 | 55 | /// Execute command 56 | /// \throw InfluxDBException when execution fails 57 | std::string execute(const std::string& cmd) override; 58 | 59 | /// Creates database used at url if it does not exists 60 | /// \throw InfluxDBException when HTTP POST fails 61 | void createDatabase() override; 62 | 63 | /// Enable Basic Authentication 64 | /// \param user username 65 | /// \param pass password 66 | void setBasicAuthentication(const std::string& user, const std::string& pass); 67 | 68 | /// Sets the API token for authentication 69 | /// \param token API token 70 | void setAuthToken(const std::string& token); 71 | 72 | /// Sets proxy 73 | void setProxy(const Proxy& proxy) override; 74 | 75 | void setVerifyCertificate(bool verify); 76 | void setTimeout(std::chrono::milliseconds timeout); 77 | 78 | private: 79 | std::string endpointUrl; 80 | std::string databaseName; 81 | cpr::Session session; 82 | }; 83 | 84 | } // namespace influxdb 85 | 86 | #endif // INFLUXDATA_TRANSPORTS_HTTP_H 87 | -------------------------------------------------------------------------------- /src/InfluxDB.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // Copyright (c) 2019 Adam Wegrzynek 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | /// 25 | /// \author Adam Wegrzynek 26 | /// 27 | 28 | #include "InfluxDB/InfluxDB.h" 29 | #include "InfluxDB/InfluxDBException.h" 30 | #include "LineProtocol.h" 31 | #include "BoostSupport.h" 32 | #include 33 | #include 34 | #include 35 | 36 | namespace influxdb 37 | { 38 | 39 | InfluxDB::InfluxDB(std::unique_ptr transport) 40 | : mPointBatch{}, 41 | mIsBatchingActivated{false}, 42 | mBatchSize{0}, 43 | mTransport(std::move(transport)), 44 | mGlobalTags{} 45 | { 46 | if (mTransport == nullptr) 47 | { 48 | throw InfluxDBException{"Transport must not be nullptr"}; 49 | } 50 | } 51 | 52 | void InfluxDB::batchOf(std::size_t size) 53 | { 54 | mBatchSize = size; 55 | mIsBatchingActivated = true; 56 | } 57 | 58 | std::size_t InfluxDB::batchSize() const 59 | { 60 | return mPointBatch.size(); 61 | } 62 | 63 | void InfluxDB::clearBatch() 64 | { 65 | mPointBatch.clear(); 66 | } 67 | 68 | void InfluxDB::flushBatch() 69 | { 70 | if (mIsBatchingActivated && !mPointBatch.empty()) 71 | { 72 | transmit(joinLineProtocolBatch()); 73 | mPointBatch.clear(); 74 | } 75 | } 76 | 77 | std::string InfluxDB::joinLineProtocolBatch() const 78 | { 79 | std::string joinedBatch; 80 | 81 | LineProtocol formatter{mGlobalTags}; 82 | for (const auto& point : mPointBatch) 83 | { 84 | joinedBatch += formatter.format(point) + "\n"; 85 | } 86 | 87 | joinedBatch.erase(std::prev(joinedBatch.end())); 88 | return joinedBatch; 89 | } 90 | 91 | 92 | void InfluxDB::addGlobalTag(std::string_view name, std::string_view value) 93 | { 94 | if (!mGlobalTags.empty()) 95 | { 96 | mGlobalTags += ","; 97 | } 98 | mGlobalTags += LineProtocol::EscapeStringElement(LineProtocol::ElementType::TagKey, name); 99 | mGlobalTags += "="; 100 | mGlobalTags += LineProtocol::EscapeStringElement(LineProtocol::ElementType::TagValue, value); 101 | } 102 | 103 | void InfluxDB::transmit(std::string&& point) 104 | { 105 | mTransport->send(std::move(point)); 106 | } 107 | 108 | void InfluxDB::write(Point&& point) 109 | { 110 | if (mIsBatchingActivated) 111 | { 112 | addPointToBatch(std::move(point)); 113 | } 114 | else 115 | { 116 | LineProtocol formatter{mGlobalTags}; 117 | transmit(formatter.format(point)); 118 | } 119 | } 120 | 121 | void InfluxDB::write(std::vector&& points) 122 | { 123 | if (mIsBatchingActivated) 124 | { 125 | for (auto&& point : points) 126 | { 127 | addPointToBatch(std::move(point)); 128 | } 129 | } 130 | else 131 | { 132 | std::string lineProtocol; 133 | LineProtocol formatter{mGlobalTags}; 134 | 135 | for (const auto& point : points) 136 | { 137 | lineProtocol += formatter.format(point) + "\n"; 138 | } 139 | 140 | lineProtocol.erase(std::prev(lineProtocol.end())); 141 | transmit(std::move(lineProtocol)); 142 | } 143 | } 144 | 145 | std::string InfluxDB::execute(const std::string& cmd) 146 | { 147 | return mTransport->execute(cmd); 148 | } 149 | 150 | void InfluxDB::addPointToBatch(Point&& point) 151 | { 152 | mPointBatch.emplace_back(std::move(point)); 153 | 154 | if (mPointBatch.size() >= mBatchSize) 155 | { 156 | flushBatch(); 157 | } 158 | } 159 | 160 | std::vector InfluxDB::query(const std::string& query) 161 | { 162 | return internal::queryImpl(mTransport.get(), query); 163 | } 164 | 165 | void InfluxDB::createDatabaseIfNotExists() 166 | { 167 | mTransport->createDatabase(); 168 | } 169 | 170 | } // namespace influxdb 171 | -------------------------------------------------------------------------------- /src/InfluxDBBuilder.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "InfluxDB/InfluxDBBuilder.h" 24 | #include "HTTP.h" 25 | 26 | namespace influxdb 27 | { 28 | InfluxDBBuilder::InfluxDBBuilder(std::unique_ptr impl) 29 | : transport(std::move(impl)) 30 | { 31 | } 32 | 33 | std::unique_ptr InfluxDBBuilder::connect() 34 | { 35 | return std::make_unique(std::move(transport)); 36 | } 37 | 38 | InfluxDBBuilder&& InfluxDBBuilder::setBasicAuthentication(const std::string& user, const std::string& pass) 39 | { 40 | dynamic_cast(*transport).setBasicAuthentication(user, pass); 41 | return std::move(*this); 42 | } 43 | 44 | InfluxDBBuilder&& InfluxDBBuilder::setAuthToken(const std::string& token) 45 | { 46 | dynamic_cast(*transport).setAuthToken(token); 47 | return std::move(*this); 48 | } 49 | 50 | InfluxDBBuilder&& InfluxDBBuilder::setProxy(const Proxy& proxy) 51 | { 52 | dynamic_cast(*transport).setProxy(proxy); 53 | return std::move(*this); 54 | } 55 | 56 | InfluxDBBuilder&& InfluxDBBuilder::setTimeout(std::chrono::milliseconds timeout) 57 | { 58 | dynamic_cast(*transport).setTimeout(timeout); 59 | return std::move(*this); 60 | } 61 | 62 | InfluxDBBuilder&& InfluxDBBuilder::setVerifyCertificate(bool verify) 63 | { 64 | dynamic_cast(*transport).setVerifyCertificate(verify); 65 | return std::move(*this); 66 | } 67 | 68 | InfluxDBBuilder InfluxDBBuilder::http(const std::string& url) 69 | { 70 | return InfluxDBBuilder{std::make_unique(url)}; 71 | } 72 | 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/InfluxDBFactory.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // Copyright (c) 2019 Adam Wegrzynek 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | /// 25 | /// \author Adam Wegrzynek 26 | /// 27 | 28 | #include "InfluxDB/InfluxDBFactory.h" 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "InfluxDB/InfluxDBException.h" 34 | #include "UriParser.h" 35 | #include "HTTP.h" 36 | #include "BoostSupport.h" 37 | 38 | namespace influxdb 39 | { 40 | namespace internal 41 | { 42 | std::unique_ptr withHttpTransport(const http::url& uri) 43 | { 44 | auto transport = std::make_unique(uri.url); 45 | if (!uri.user.empty() && !uri.password.empty()) 46 | { 47 | transport->setBasicAuthentication(uri.user, uri.password); 48 | } 49 | else if (!uri.password.empty()) 50 | { 51 | transport->setAuthToken(uri.password); 52 | } 53 | return transport; 54 | } 55 | 56 | } 57 | 58 | std::unique_ptr InfluxDBFactory::GetTransport(const std::string& url) 59 | { 60 | static const std::map(const http::url&)>> map = { 61 | {"udp", internal::withUdpTransport}, 62 | {"tcp", internal::withTcpTransport}, 63 | {"http", internal::withHttpTransport}, 64 | {"https", internal::withHttpTransport}, 65 | {"unix", internal::withUnixSocketTransport}, 66 | }; 67 | 68 | auto urlCopy = url; 69 | http::url parsedUrl = http::ParseHttpUrl(urlCopy); 70 | if (parsedUrl.protocol.empty()) 71 | { 72 | throw InfluxDBException("Ill-formed URI"); 73 | } 74 | 75 | const auto iterator = map.find(parsedUrl.protocol); 76 | if (iterator == map.end()) 77 | { 78 | throw InfluxDBException("Unrecognized backend " + parsedUrl.protocol); 79 | } 80 | 81 | return iterator->second(parsedUrl); 82 | } 83 | 84 | std::unique_ptr InfluxDBFactory::Get(const std::string& url) 85 | { 86 | return std::make_unique(InfluxDBFactory::GetTransport(url)); 87 | } 88 | 89 | std::unique_ptr InfluxDBFactory::Get(const std::string& url, const Proxy& proxy) 90 | { 91 | auto transport = InfluxDBFactory::GetTransport(url); 92 | transport->setProxy(proxy); 93 | return std::make_unique(std::move(transport)); 94 | } 95 | 96 | } // namespace influxdb 97 | -------------------------------------------------------------------------------- /src/LineProtocol.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "LineProtocol.h" 24 | 25 | #include 26 | #include 27 | 28 | namespace influxdb 29 | { 30 | namespace 31 | { 32 | template 33 | struct overloaded : Ts... 34 | { 35 | using Ts::operator()...; 36 | }; 37 | template 38 | overloaded(Ts...) -> overloaded; 39 | 40 | void appendIfNotEmpty(std::string& dest, const std::string& value, char separator) 41 | { 42 | if (!value.empty()) 43 | { 44 | dest.append(std::string{separator}).append(value); 45 | } 46 | } 47 | 48 | std::string escapeCharacters(std::string_view input, const std::string& escapedChars) 49 | { 50 | static const std::string escapeCharacter{"\\"}; 51 | std::string output; 52 | output.reserve(input.size()); 53 | 54 | std::size_t searchStartPos{0}; 55 | // Find the first character that needs to be escaped 56 | std::size_t escapedCharacterPos{input.find_first_of(escapedChars, searchStartPos)}; 57 | while (escapedCharacterPos != std::string::npos) 58 | { 59 | // Append the characters between the previous escaped character and the current one 60 | output.append(input, searchStartPos, escapedCharacterPos - searchStartPos); 61 | // Append the escape character and the character to be escaped 62 | output.append(escapeCharacter).append(1, input[escapedCharacterPos]); 63 | // Update the search start index to the character after the escaped character 64 | searchStartPos = escapedCharacterPos + 1; 65 | // Find the next character that needs to be escaped 66 | escapedCharacterPos = input.find_first_of(escapedChars, searchStartPos); 67 | } 68 | // Append remaining characters after the final escaped character 69 | output.append(input, searchStartPos); 70 | 71 | return output; 72 | } 73 | 74 | std::string formatTags(const Point::TagSet& tagsDeque) 75 | { 76 | std::string tags; 77 | bool addComma{false}; 78 | for (const auto& tag : tagsDeque) 79 | { 80 | if (addComma) 81 | { 82 | tags += ','; 83 | } 84 | tags += LineProtocol::EscapeStringElement(LineProtocol::ElementType::TagKey, tag.first); 85 | tags += '='; 86 | tags += LineProtocol::EscapeStringElement(LineProtocol::ElementType::TagValue, tag.second); 87 | addComma = true; 88 | } 89 | 90 | return tags; 91 | } 92 | 93 | std::string formatFields(const Point::FieldSet& fieldsDeque) 94 | { 95 | std::stringstream convert; 96 | convert << std::setprecision(Point::floatsPrecision) << std::fixed; 97 | bool addComma{false}; 98 | for (const auto& field : fieldsDeque) 99 | { 100 | if (addComma) 101 | { 102 | convert << ','; 103 | } 104 | 105 | convert << LineProtocol::EscapeStringElement(LineProtocol::ElementType::FieldKey, field.first) << "="; 106 | std::visit(overloaded{ 107 | [&convert](int v) 108 | { convert << v << 'i'; }, 109 | [&convert](long long int v) 110 | { convert << v << 'i'; }, 111 | [&convert](double v) 112 | { convert << v; }, 113 | [&convert](const std::string& v) 114 | { convert << '"' << LineProtocol::EscapeStringElement(LineProtocol::ElementType::FieldValue, v) << '"'; }, 115 | [&convert](bool v) 116 | { convert << (v ? "true" : "false"); }, 117 | [&convert](unsigned int v) 118 | { convert << v << 'u'; }, 119 | [&convert](unsigned long long int v) 120 | { convert << v << 'u'; }, 121 | }, 122 | field.second); 123 | addComma = true; 124 | } 125 | 126 | return convert.str(); 127 | } 128 | } 129 | LineProtocol::LineProtocol() 130 | : LineProtocol(std::string{}) 131 | { 132 | } 133 | 134 | LineProtocol::LineProtocol(const std::string& tags) 135 | : globalTags(tags) 136 | { 137 | } 138 | 139 | std::string LineProtocol::format(const Point& point) const 140 | { 141 | std::string line{LineProtocol::EscapeStringElement(LineProtocol::ElementType::Measurement, point.getName())}; 142 | appendIfNotEmpty(line, globalTags, ','); 143 | appendIfNotEmpty(line, formatTags(point.getTagSet()), ','); 144 | appendIfNotEmpty(line, formatFields(point.getFieldSet()), ' '); 145 | 146 | return line.append(" ") 147 | .append(std::to_string(std::chrono::duration_cast(point.getTimestamp().time_since_epoch()).count())); 148 | } 149 | 150 | std::string LineProtocol::EscapeStringElement(LineProtocol::ElementType type, std::string_view element) 151 | { 152 | // https://docs.influxdata.com/influxdb/cloud/reference/syntax/line-protocol/#special-characters 153 | static const std::string commaAndSpace{", "}; 154 | static const std::string commaEqualsAndSpace{",= "}; 155 | static const std::string doubleQuoteAndBackslash{R"("\)"}; 156 | 157 | switch (type) 158 | { 159 | case ElementType::Measurement: 160 | return escapeCharacters(element, commaAndSpace); 161 | case ElementType::TagKey: 162 | case ElementType::TagValue: 163 | case ElementType::FieldKey: 164 | return escapeCharacters(element, commaEqualsAndSpace); 165 | case ElementType::FieldValue: 166 | return escapeCharacters(element, doubleQuoteAndBackslash); 167 | } 168 | return std::string{element}; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/LineProtocol.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #include "InfluxDB/Point.h" 26 | 27 | #include 28 | 29 | namespace influxdb 30 | { 31 | class LineProtocol 32 | { 33 | public: 34 | LineProtocol(); 35 | // Caller must ensure that the tags string is correctly escaped 36 | explicit LineProtocol(const std::string& tags); 37 | 38 | std::string format(const Point& point) const; 39 | 40 | enum class ElementType 41 | { 42 | Measurement, 43 | TagKey, 44 | TagValue, 45 | FieldKey, 46 | FieldValue 47 | }; 48 | 49 | // Escapes special characters in a string element according to the 50 | // InfluxDB line protocol specification. 51 | // https://docs.influxdata.com/influxdb/cloud/reference/syntax/line-protocol/#special-characters 52 | static std::string EscapeStringElement(ElementType type, std::string_view stringElement); 53 | 54 | private: 55 | std::string globalTags; 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /src/NoBoostSupport.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // Copyright (c) 2019 Adam Wegrzynek 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | #include "BoostSupport.h" 25 | #include "InfluxDB/InfluxDBException.h" 26 | 27 | namespace influxdb::internal 28 | { 29 | std::vector queryImpl([[maybe_unused]] Transport* transport, [[maybe_unused]] const std::string& query) 30 | { 31 | throw InfluxDBException("Query requires Boost"); 32 | } 33 | 34 | std::unique_ptr withUdpTransport([[maybe_unused]] const http::url& uri) 35 | { 36 | throw InfluxDBException("UDP transport requires Boost"); 37 | } 38 | 39 | std::unique_ptr withTcpTransport([[maybe_unused]] const http::url& uri) 40 | { 41 | throw InfluxDBException("TCP transport requires Boost"); 42 | } 43 | 44 | std::unique_ptr withUnixSocketTransport([[maybe_unused]] const http::url& uri) 45 | { 46 | throw InfluxDBException("Unix socket transport requires Boost"); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Point.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // Copyright (c) 2019 Adam Wegrzynek 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | /// 25 | /// \author Adam Wegrzynek 26 | /// 27 | 28 | #include "InfluxDB/Point.h" 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | namespace influxdb 35 | { 36 | namespace 37 | { 38 | template 39 | struct overloaded : Ts... 40 | { 41 | using Ts::operator()...; 42 | }; 43 | template 44 | overloaded(Ts...) -> overloaded; 45 | } 46 | 47 | Point::Point(const std::string& measurement) 48 | : mMeasurement(measurement), mTimestamp(std::chrono::system_clock::now()), mTags({}), mFields({}) 49 | { 50 | } 51 | 52 | Point&& Point::addField(std::string_view name, const Point::FieldValue& value) 53 | { 54 | if (name.empty()) 55 | { 56 | return std::move(*this); 57 | } 58 | 59 | mFields.emplace_back(std::make_pair(name, value)); 60 | return std::move(*this); 61 | } 62 | 63 | Point&& Point::addTag(std::string_view key, std::string_view value) 64 | { 65 | if (key.empty() || value.empty()) 66 | { 67 | return std::move(*this); 68 | } 69 | 70 | mTags.emplace_back(std::make_pair(key, value)); 71 | return std::move(*this); 72 | } 73 | 74 | Point&& Point::setTimestamp(std::chrono::time_point timestamp) 75 | { 76 | mTimestamp = timestamp; 77 | return std::move(*this); 78 | } 79 | 80 | std::string Point::getName() const 81 | { 82 | return mMeasurement; 83 | } 84 | 85 | std::chrono::time_point Point::getTimestamp() const 86 | { 87 | return mTimestamp; 88 | } 89 | 90 | std::string Point::getFields() const 91 | { 92 | std::stringstream convert; 93 | convert << std::setprecision(floatsPrecision) << std::fixed; 94 | bool addComma{false}; 95 | for (const auto& field : mFields) 96 | { 97 | if (addComma) 98 | { 99 | convert << ','; 100 | } 101 | 102 | convert << field.first << "="; 103 | std::visit(overloaded{ 104 | [&convert](int v) 105 | { convert << v << 'i'; }, 106 | [&convert](long long int v) 107 | { convert << v << 'i'; }, 108 | [&convert](double v) 109 | { convert << v; }, 110 | [&convert](const std::string& v) 111 | { convert << '"' << v << '"'; }, 112 | [&convert](bool v) 113 | { convert << (v ? "true" : "false"); }, 114 | [&convert](unsigned int v) 115 | { convert << v << 'u'; }, 116 | [&convert](unsigned long long int v) 117 | { convert << v << 'u'; }, 118 | }, 119 | field.second); 120 | addComma = true; 121 | } 122 | 123 | return convert.str(); 124 | } 125 | 126 | const Point::FieldSet& Point::getFieldSet() const 127 | { 128 | return mFields; 129 | } 130 | 131 | std::string Point::getTags() const 132 | { 133 | if (mTags.empty()) 134 | { 135 | return ""; 136 | } 137 | 138 | std::string tags; 139 | for (const auto& tag : mTags) 140 | { 141 | tags += ","; 142 | tags += tag.first; 143 | tags += "="; 144 | tags += tag.second; 145 | } 146 | 147 | return tags.substr(1, tags.size()); 148 | } 149 | 150 | const Point::TagSet& Point::getTagSet() const 151 | { 152 | return mTags; 153 | } 154 | 155 | } // namespace influxdb 156 | -------------------------------------------------------------------------------- /src/Proxy.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "InfluxDB/Proxy.h" 24 | 25 | namespace influxdb 26 | { 27 | Proxy::Proxy(const std::string& proxy, Proxy::Auth auth) 28 | : proxy_(proxy), 29 | auth_(auth) 30 | { 31 | } 32 | 33 | Proxy::Proxy(const std::string& proxy) 34 | : proxy_(proxy), auth_({}) 35 | { 36 | } 37 | 38 | 39 | const std::string& Proxy::getProxy() const 40 | { 41 | return proxy_; 42 | } 43 | 44 | std::optional Proxy::getAuthentication() const 45 | { 46 | return auth_; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/TCP.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2021 Felix Moessbauer 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | /// 24 | /// \author Felix Moessbauer 25 | /// 26 | 27 | #include "TCP.h" 28 | #include "InfluxDB/InfluxDBException.h" 29 | #include 30 | 31 | namespace influxdb::transports 32 | { 33 | TCP::TCP(const std::string& hostname, int port) 34 | : mSocket(mIoContext) 35 | { 36 | boost::asio::ip::tcp::resolver resolver(mIoContext); 37 | mEndpoint = *(resolver 38 | .resolve(boost::asio::ip::tcp::v4(), 39 | hostname, 40 | std::to_string(port), 41 | boost::asio::ip::resolver_query_base::passive) 42 | .cbegin()); 43 | mSocket.open(mEndpoint.protocol()); 44 | reconnect(); 45 | } 46 | 47 | bool TCP::is_connected() const 48 | { 49 | return mSocket.is_open(); 50 | } 51 | 52 | void TCP::reconnect() 53 | { 54 | mSocket.connect(mEndpoint); 55 | mSocket.wait(boost::asio::ip::tcp::socket::wait_write); 56 | } 57 | 58 | void TCP::send(std::string&& message) 59 | { 60 | try 61 | { 62 | message.append("\n"); 63 | const size_t written = mSocket.write_some(boost::asio::buffer(message, message.size())); 64 | if (written != message.size()) 65 | { 66 | throw InfluxDBException("Error while transmitting data"); 67 | } 68 | } 69 | catch (const boost::system::system_error& e) 70 | { 71 | throw InfluxDBException(e.what()); 72 | } 73 | } 74 | 75 | } // namespace influxdb::transports 76 | -------------------------------------------------------------------------------- /src/TCP.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2021 Felix Moessbauer 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | /// 24 | /// \author Felix Moessbauer 25 | /// 26 | 27 | #ifndef INFLUXDATA_TRANSPORTS_TCP_H 28 | #define INFLUXDATA_TRANSPORTS_TCP_H 29 | 30 | #include "InfluxDB/Transport.h" 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | namespace influxdb::transports 37 | { 38 | 39 | /// \brief TCP transport 40 | class TCP : public Transport 41 | { 42 | public: 43 | /// Constructor 44 | TCP(const std::string& hostname, int port); 45 | 46 | /// Sends blob via TCP 47 | void send(std::string&& message) override; 48 | 49 | /// check if socket is connected 50 | bool is_connected() const; 51 | 52 | /// reconnect socket 53 | void reconnect(); 54 | 55 | private: 56 | /// Boost Asio I/O functionality 57 | boost::asio::io_context mIoContext; 58 | 59 | /// TCP socket 60 | boost::asio::ip::tcp::socket mSocket; 61 | 62 | /// TCP endpoint 63 | boost::asio::ip::tcp::endpoint mEndpoint; 64 | }; 65 | 66 | } // namespace influxdb::transports 67 | 68 | #endif // INFLUXDATA_TRANSPORTS_TCP_H 69 | -------------------------------------------------------------------------------- /src/UDP.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // Copyright (c) 2019 Adam Wegrzynek 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | /// 25 | /// \author Adam Wegrzynek 26 | /// 27 | 28 | #include "UDP.h" 29 | #include "InfluxDB/InfluxDBException.h" 30 | #include 31 | 32 | namespace influxdb::transports 33 | { 34 | 35 | UDP::UDP(const std::string& hostname, int port) 36 | : mSocket(mIoContext, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0)) 37 | { 38 | boost::asio::ip::udp::resolver resolver(mIoContext); 39 | mEndpoint = *(resolver 40 | .resolve(boost::asio::ip::udp::v4(), 41 | hostname, 42 | std::to_string(port), 43 | boost::asio::ip::resolver_query_base::passive) 44 | .cbegin()); 45 | } 46 | 47 | void UDP::send(std::string&& message) 48 | { 49 | try 50 | { 51 | mSocket.send_to(boost::asio::buffer(message, message.size()), mEndpoint); 52 | } 53 | catch (const boost::system::system_error& e) 54 | { 55 | throw InfluxDBException(e.what()); 56 | } 57 | } 58 | 59 | } // namespace influxdb::transports 60 | -------------------------------------------------------------------------------- /src/UDP.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // Copyright (c) 2019 Adam Wegrzynek 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | /// 25 | /// \author Adam Wegrzynek 26 | /// 27 | 28 | #ifndef INFLUXDATA_TRANSPORTS_UDP_H 29 | #define INFLUXDATA_TRANSPORTS_UDP_H 30 | 31 | #include "InfluxDB/Transport.h" 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | namespace influxdb::transports 38 | { 39 | 40 | /// \brief UDP transport 41 | class UDP : public Transport 42 | { 43 | public: 44 | /// Constructor 45 | UDP(const std::string& hostname, int port); 46 | 47 | /// Sends blob via UDP 48 | void send(std::string&& message) override; 49 | 50 | private: 51 | /// Boost Asio I/O functionality 52 | boost::asio::io_context mIoContext; 53 | 54 | /// UDP socket 55 | boost::asio::ip::udp::socket mSocket; 56 | 57 | /// UDP endpoint 58 | boost::asio::ip::udp::endpoint mEndpoint; 59 | }; 60 | 61 | } // namespace influxdb::transports 62 | 63 | #endif // INFLUXDATA_TRANSPORTS_UDP_H 64 | -------------------------------------------------------------------------------- /src/UnixSocket.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // Copyright (c) 2019 Adam Wegrzynek 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | /// 25 | /// \author Adam Wegrzynek 26 | /// 27 | 28 | #include "UnixSocket.h" 29 | #include "InfluxDB/InfluxDBException.h" 30 | #include 31 | 32 | namespace influxdb::transports 33 | { 34 | #if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) 35 | 36 | UnixSocket::UnixSocket(const std::string& socketPath) 37 | : mSocket(mIoContext), mEndpoint(socketPath) 38 | { 39 | mSocket.open(); 40 | } 41 | 42 | void UnixSocket::send(std::string&& message) 43 | { 44 | try 45 | { 46 | mSocket.send_to(boost::asio::buffer(message, message.size()), mEndpoint); 47 | } 48 | catch (const boost::system::system_error& e) 49 | { 50 | throw InfluxDBException(e.what()); 51 | } 52 | } 53 | 54 | #else 55 | 56 | UnixSocket::UnixSocket(const std::string&) 57 | { 58 | throw InfluxDBException{"Unix socket not supported on this system"}; 59 | } 60 | 61 | void UnixSocket::send(std::string&&) 62 | { 63 | throw InfluxDBException{"Unix socket not supported on this system"}; 64 | } 65 | 66 | #endif // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) 67 | 68 | } // namespace influxdb::transports 69 | -------------------------------------------------------------------------------- /src/UnixSocket.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // Copyright (c) 2019 Adam Wegrzynek 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | /// 25 | /// \author Adam Wegrzynek 26 | /// 27 | 28 | #ifndef INFLUXDATA_TRANSPORTS_UNIX_H 29 | #define INFLUXDATA_TRANSPORTS_UNIX_H 30 | 31 | #include "InfluxDB/Transport.h" 32 | 33 | #include 34 | #include 35 | 36 | namespace influxdb::transports 37 | { 38 | 39 | /// \brief Unix datagram socket transport 40 | class UnixSocket : public Transport 41 | { 42 | public: 43 | explicit UnixSocket(const std::string& socketPath); 44 | 45 | /// \param message r-value string formated 46 | void send(std::string&& message) override; 47 | 48 | private: 49 | /// Boost Asio I/O functionality 50 | boost::asio::io_context mIoContext; 51 | #if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) 52 | /// Unix socket 53 | boost::asio::local::datagram_protocol::socket mSocket; 54 | 55 | /// Unix endpoint 56 | boost::asio::local::datagram_protocol::endpoint mEndpoint; 57 | #endif // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) 58 | }; 59 | 60 | } // namespace influxdb::transports 61 | 62 | #endif // INFLUXDATA_TRANSPORTS_UNIX_H 63 | -------------------------------------------------------------------------------- /src/UriParser.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Covenant Eyes 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 4 | // software and associated documentation files (the "Software"), to deal in the Software 5 | // without restriction, including without limitation the rights to use, copy, modify, 6 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 7 | // permit persons to whom the Software is furnished to do so, subject to the following 8 | // conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all copies 11 | // or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 14 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 15 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 16 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 17 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 18 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | #ifndef INFLUXDATA_HTTPPARSER_H 21 | #define INFLUXDATA_HTTPPARSER_H 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | 28 | namespace http 29 | { 30 | struct url 31 | { 32 | std::string protocol, user, password, host, path, search, url; 33 | int port; 34 | }; 35 | 36 | 37 | //--- Helper Functions -------------------------------------------------------------~ 38 | static inline std::string TailSlice(std::string& subject, std::string delimiter, bool keep_delim = false) 39 | { 40 | // Chops off the delimiter and everything that follows (destructively) 41 | // returns everything after the delimiter 42 | auto delimiter_location = subject.find(delimiter); 43 | auto delimiter_length = delimiter.length(); 44 | std::string output = ""; 45 | 46 | if (delimiter_location < std::string::npos) 47 | { 48 | auto start = keep_delim ? delimiter_location : delimiter_location + delimiter_length; 49 | auto end = subject.length() - start; 50 | output = subject.substr(start, end); 51 | subject = subject.substr(0, delimiter_location); 52 | } 53 | return output; 54 | } 55 | 56 | static inline std::string HeadSlice(std::string& subject, std::string delimiter) 57 | { 58 | // Chops off the delimiter and everything that precedes (destructively) 59 | // returns everthing before the delimeter 60 | auto delimiter_location = subject.find(delimiter); 61 | auto delimiter_length = delimiter.length(); 62 | std::string output = ""; 63 | if (delimiter_location < std::string::npos) 64 | { 65 | output = subject.substr(0, delimiter_location); 66 | subject = subject.substr(delimiter_location + delimiter_length, subject.length() - (delimiter_location + delimiter_length)); 67 | } 68 | return output; 69 | } 70 | 71 | 72 | //--- Extractors -------------------------------------------------------------------~ 73 | static inline int ExtractPort(std::string& hostport) 74 | { 75 | int port; 76 | std::string portstring = TailSlice(hostport, ":"); 77 | try 78 | { 79 | port = atoi(portstring.c_str()); 80 | } 81 | catch (const std::exception&) 82 | { 83 | port = -1; 84 | } 85 | return port; 86 | } 87 | 88 | static inline std::string ExtractPath(std::string& in) 89 | { 90 | return TailSlice(in, "/", true); 91 | } 92 | static inline std::string ExtractProtocol(std::string& in) 93 | { 94 | return HeadSlice(in, "://"); 95 | } 96 | static inline std::string ExtractSearch(std::string& in) 97 | { 98 | return TailSlice(in, "?"); 99 | } 100 | static inline std::string ExtractPassword(std::string& userpass) 101 | { 102 | return TailSlice(userpass, ":"); 103 | } 104 | static inline std::string ExtractUserpass(std::string& in) 105 | { 106 | return HeadSlice(in, "@"); 107 | } 108 | 109 | 110 | //--- Public Interface -------------------------------------------------------------~ 111 | static inline url ParseHttpUrl(std::string& in) 112 | { 113 | const auto url = in; 114 | const auto protocol = ExtractProtocol(in); 115 | const auto search = ExtractSearch(in); 116 | const auto path = ExtractPath(in); 117 | std::string userpass = ExtractUserpass(in); 118 | 119 | auto [password, user] = [](auto str) 120 | { 121 | if (str.find(":") != std::string::npos) 122 | { 123 | return std::make_pair(ExtractPassword(str), str); 124 | } 125 | return std::make_pair(str, std::string{""}); 126 | }(userpass); 127 | const auto port = ExtractPort(in); 128 | const auto host = in; 129 | 130 | return {protocol, user, password, host, path, search, url, port}; 131 | } 132 | } 133 | #endif 134 | -------------------------------------------------------------------------------- /test/BoostSupportTest.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "BoostSupport.h" 24 | #include "InfluxDB/InfluxDBException.h" 25 | #include "mock/TransportMock.h" 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace influxdb::test 33 | { 34 | TEST_CASE("With UDP returns transport", "[BoostSupportTest]") 35 | { 36 | CHECK(internal::withUdpTransport(http::url{}) != nullptr); 37 | } 38 | 39 | TEST_CASE("With Unix socket returns transport", "[BoostSupportTest]") 40 | { 41 | CHECK(internal::withUnixSocketTransport(http::url{}) != nullptr); 42 | } 43 | 44 | TEST_CASE("UDP transport throws on create database", "[BoostSupportTest]") 45 | { 46 | auto udp = internal::withUdpTransport(http::url{}); 47 | CHECK_THROWS_AS(udp->createDatabase(), std::runtime_error); 48 | } 49 | 50 | TEST_CASE("UDP transport throws on proxy", "[BoostSupport]") 51 | { 52 | auto udp = internal::withUdpTransport(http::url{}); 53 | CHECK_THROWS_AS(udp->setProxy(Proxy{"udp://should-throw"}), std::runtime_error); 54 | } 55 | 56 | TEST_CASE("UDP transport throws on execute", "[BoostSupport]") 57 | { 58 | auto udp = internal::withUdpTransport(http::url{}); 59 | CHECK_THROWS_AS(udp->execute("show databases"), std::runtime_error); 60 | } 61 | 62 | TEST_CASE("Unix socket transport throws on create database", "[BoostSupportTest]") 63 | { 64 | auto unix = internal::withUnixSocketTransport(http::url{}); 65 | CHECK_THROWS_AS(unix->createDatabase(), std::runtime_error); 66 | } 67 | 68 | TEST_CASE("Unix socket transport throws on proxy", "[BoostSupportTest]") 69 | { 70 | auto unix = internal::withUnixSocketTransport(http::url{}); 71 | CHECK_THROWS_AS(unix->setProxy(Proxy{"unix:///tmp/should_throw"}), std::runtime_error); 72 | } 73 | 74 | TEST_CASE("Unix socket transport throws on execute", "[BoostSupportTest]") 75 | { 76 | auto unix = internal::withUnixSocketTransport(http::url{}); 77 | CHECK_THROWS_AS(unix->execute("show databases"), std::runtime_error); 78 | } 79 | 80 | TEST_CASE("Query is passed to transport", "[BoostSupportTest]") 81 | { 82 | TransportMock transport; 83 | REQUIRE_CALL(transport, query("SELECT * from test WHERE host = 'localhost'")) 84 | .RETURN(R"({"results":[{"statement_id":0}]})"); 85 | 86 | internal::queryImpl(&transport, "SELECT * from test WHERE host = 'localhost'"); 87 | } 88 | 89 | TEST_CASE("Query throws if transport throws", "[BoostSupportTest]") 90 | { 91 | using trompeloeil::_; 92 | 93 | TransportMock transport; 94 | ALLOW_CALL(transport, query(_)).THROW(InfluxDBException{"Intentional"}); 95 | 96 | CHECK_THROWS_AS(internal::queryImpl(&transport, "select should throw"), InfluxDBException); 97 | } 98 | 99 | TEST_CASE("Query returns empty if empty result", "[BoostSupportTest]") 100 | { 101 | using trompeloeil::_; 102 | 103 | TransportMock transport; 104 | ALLOW_CALL(transport, query(_)).RETURN(R"({"results":[]})"); 105 | 106 | CHECK(internal::queryImpl(&transport, "SELECT * from test").empty()); 107 | } 108 | 109 | TEST_CASE("Query returns point of single result", "[BoostSupportTest]") 110 | { 111 | using trompeloeil::_; 112 | 113 | std::istringstream in{"2021-01-01T00:11:22.123456789Z"}; 114 | std::chrono::system_clock::time_point expectedTimeStamp{}; 115 | in >> date::parse("%FT%T%Z", expectedTimeStamp); 116 | 117 | TransportMock transport; 118 | ALLOW_CALL(transport, query(_)) 119 | .RETURN(R"({"results":[{"statement_id":0,)" 120 | R"("series":[{"name":"unittest","columns":["time","host","value"],)" 121 | R"("values":[["2021-01-01T00:11:22.123456789Z","localhost",112233]]}]}]})"); 122 | 123 | const auto result = internal::queryImpl(&transport, "SELECT * from test"); 124 | CHECK(result.size() == 1); 125 | const auto point = result[0]; 126 | CHECK(point.getName() == "unittest"); 127 | CHECK(point.getTimestamp() == expectedTimeStamp); 128 | CHECK(point.getTags() == "host=localhost"); 129 | CHECK(point.getFields() == "value=112233.000000000000000000"); 130 | } 131 | 132 | TEST_CASE("Query returns points of multiple results", "[BoostSupportTest]") 133 | { 134 | using trompeloeil::_; 135 | 136 | TransportMock transport; 137 | ALLOW_CALL(transport, query(_)) 138 | .RETURN(R"({"results":[{"statement_id":0,)" 139 | R"("series":[{"name":"unittest","columns":["time","host","value"],)" 140 | R"("values":[["2021-01-01:11:22.000000000Z","host-0",100],)" 141 | R"(["2021-01-01T00:11:23.560000000Z","host-1",30],)" 142 | R"(["2021-01-01T00:11:24.780000000Z","host-2",54]]}]}]})"); 143 | 144 | const auto result = internal::queryImpl(&transport, "SELECT * from test"); 145 | CHECK(result.size() == 3); 146 | CHECK(result[0].getName() == "unittest"); 147 | CHECK(result[0].getTags() == "host=host-0"); 148 | CHECK(result[0].getFields() == "value=100.000000000000000000"); 149 | CHECK(result[1].getName() == "unittest"); 150 | CHECK(result[1].getTags() == "host=host-1"); 151 | CHECK(result[1].getFields() == "value=30.000000000000000000"); 152 | CHECK(result[2].getName() == "unittest"); 153 | CHECK(result[2].getTags() == "host=host-2"); 154 | CHECK(result[2].getFields() == "value=54.000000000000000000"); 155 | } 156 | 157 | TEST_CASE("Query throws on invalid result", "[BoostSupportTest]") 158 | { 159 | using trompeloeil::_; 160 | 161 | TransportMock transport; 162 | ALLOW_CALL(transport, query(_)) 163 | .RETURN(R"({"invalid-results":[]})"); 164 | 165 | CHECK_THROWS_AS(internal::queryImpl(&transport, "SELECT * from test"), boost::property_tree::ptree_bad_path); 166 | } 167 | 168 | TEST_CASE("Query is safe to empty name", "[BoostSupportTest]") 169 | { 170 | using trompeloeil::_; 171 | 172 | TransportMock transport; 173 | ALLOW_CALL(transport, query(_)) 174 | .RETURN(R"({"results":[{"statement_id":0,"series":[{"columns":["time","host","value"],)" 175 | R"("values":[["2021-01-01:11:22.000000000Z","x",8]]}]}]})"); 176 | 177 | const auto result = internal::queryImpl(&transport, "SELECT * from test"); 178 | CHECK(result.size() == 1); 179 | CHECK(result[0].getName() == ""); 180 | CHECK(result[0].getTags() == "host=x"); 181 | } 182 | 183 | TEST_CASE("Query reads optional tags element", "[BoostSupportTest]") 184 | { 185 | using trompeloeil::_; 186 | 187 | TransportMock transport; 188 | ALLOW_CALL(transport, query(_)) 189 | .RETURN(R"({"results":[{"statement_id":0,"series":[{"name":"x","tags":{"type":"sp"},"columns":["time","value"],)" 190 | R"("values":[["2022-01-01:01:02.000000000ZZ",99]]}]}]})"); 191 | 192 | const auto result = internal::queryImpl(&transport, "SELECT * from test"); 193 | CHECK(result.size() == 1); 194 | CHECK(result[0].getTags() == "type=sp"); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | find_package(Catch2 REQUIRED) 3 | find_package(trompeloeil REQUIRED) 4 | 5 | if (NOT TARGET trompeloeil::trompeloeil) 6 | add_library(trompeloeil::trompeloeil INTERFACE IMPORTED) 7 | target_link_libraries(trompeloeil::trompeloeil INTERFACE trompeloeil) 8 | endif() 9 | 10 | if (MSVC) 11 | target_compile_definitions(trompeloeil::trompeloeil INTERFACE NOMINMAX) 12 | endif() 13 | 14 | add_subdirectory("mock") 15 | 16 | 17 | function(add_unittest name) 18 | set(multiValueArgs DEPENDS) 19 | cmake_parse_arguments(TEST_OPTION "" "" ${multiValueArgs} ${ARGN}) 20 | 21 | add_executable(${name} ${name}.cxx) 22 | target_link_libraries(${name} PRIVATE 23 | ${TEST_OPTION_DEPENDS} 24 | Catch2::Catch2WithMain 25 | trompeloeil::trompeloeil 26 | Threads::Threads 27 | ) 28 | target_include_directories(${name} 29 | PRIVATE 30 | ${CMAKE_SOURCE_DIR}/src 31 | ${CMAKE_CURRENT_SOURCE_DIR}/mock 32 | ) 33 | add_test(NAME ${name} COMMAND ${name}) 34 | endfunction() 35 | 36 | add_unittest(PointTest DEPENDS InfluxDB) 37 | add_unittest(LineProtocolTest DEPENDS InfluxDB InfluxDB-Internal) 38 | add_unittest(InfluxDBTest DEPENDS InfluxDB) 39 | add_unittest(InfluxDBFactoryTest DEPENDS InfluxDB) 40 | add_unittest(ProxyTest DEPENDS InfluxDB) 41 | add_unittest(HttpTest DEPENDS InfluxDB-Core InfluxDB-Internal InfluxDB-BoostSupport CprMock Threads::Threads) 42 | add_unittest(UriParserTest) 43 | 44 | add_unittest(NoBoostSupportTest) 45 | target_sources(NoBoostSupportTest PRIVATE ${PROJECT_SOURCE_DIR}/src/NoBoostSupport.cxx) 46 | target_link_libraries(NoBoostSupportTest PRIVATE InfluxDB) 47 | 48 | if (INFLUXCXX_WITH_BOOST) 49 | add_unittest(BoostSupportTest DEPENDS InfluxDB-BoostSupport InfluxDB Boost::system date::date) 50 | endif() 51 | 52 | 53 | add_custom_target(unittest PointTest 54 | COMMAND LineProtocolTest 55 | COMMAND InfluxDBTest 56 | COMMAND InfluxDBFactoryTest 57 | COMMAND ProxyTest 58 | COMMAND HttpTest 59 | COMMAND UriParserTest 60 | COMMAND NoBoostSupportTest 61 | COMMAND $<$,$>>:BoostSupportTest> 62 | 63 | COMMENT "Running unit tests\n\n" 64 | VERBATIM 65 | ) 66 | 67 | 68 | if (INFLUXCXX_WITH_BOOST) 69 | add_dependencies(unittest BoostSupportTest) 70 | endif() 71 | 72 | 73 | if (INFLUXCXX_SYSTEMTEST) 74 | add_subdirectory(system) 75 | endif() 76 | -------------------------------------------------------------------------------- /test/HttpTest.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "HTTP.h" 24 | #include "InfluxDB/InfluxDBException.h" 25 | #include "mock/CprMock.h" 26 | #include 27 | #include 28 | 29 | namespace influxdb::test 30 | { 31 | SessionMock sessionMock; 32 | 33 | using influxdb::transports::HTTP; 34 | using trompeloeil::_; 35 | using trompeloeil::eq; 36 | 37 | using ParamMap = std::map; 38 | 39 | cpr::Response createResponse(const cpr::ErrorCode& code, std::int32_t statusCode, const std::string& text = "") 40 | { 41 | cpr::Error error{}; 42 | error.code = code; 43 | error.message = " 26 | 27 | namespace influxdb::test 28 | { 29 | TEST_CASE("Accepts http urls", "[InfluxDBFactoryTest]") 30 | { 31 | CHECK(InfluxDBFactory::Get("http://localhost?db=test") != nullptr); 32 | CHECK(InfluxDBFactory::Get("https://localhost?db=test") != nullptr); 33 | 34 | CHECK(InfluxDBFactory::Get("http://localhost:8086?db=test") != nullptr); 35 | CHECK(InfluxDBFactory::Get("https://localhost:8086?db=test") != nullptr); 36 | 37 | CHECK(InfluxDBFactory::Get("https://localhost/?db=test") != nullptr); 38 | CHECK(InfluxDBFactory::Get("https://localhost:8086/?db=test") != nullptr); 39 | } 40 | 41 | TEST_CASE("Accepts http urls with authentication", "[InfluxDBFactoryTest]") 42 | { 43 | CHECK(InfluxDBFactory::Get("http://user:pass@localhost?db=test") != nullptr); 44 | } 45 | 46 | TEST_CASE("Throws on unrecognised backend", "[InfluxDBFactoryTest]") 47 | { 48 | CHECK_THROWS_AS(InfluxDBFactory::Get("httpX://localhost:8086?db=test"), InfluxDBException); 49 | } 50 | 51 | TEST_CASE("Throws on malformed url", "[InfluxDBFactoryTest]") 52 | { 53 | CHECK_THROWS_AS(InfluxDBFactory::Get("localhost:8086?db=test"), InfluxDBException); 54 | } 55 | 56 | TEST_CASE("Throws on missing database", "[InfluxDBFactoryTest]") 57 | { 58 | CHECK_THROWS_AS(InfluxDBFactory::Get("http://localhost:8086"), InfluxDBException); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /test/InfluxDBTest.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "InfluxDB/InfluxDB.h" 24 | #include "InfluxDB/InfluxDBException.h" 25 | #include "mock/TransportMock.h" 26 | #include 27 | #include 28 | 29 | namespace influxdb::test 30 | { 31 | namespace 32 | { 33 | constexpr std::chrono::time_point ignoreTimestamp(std::chrono::milliseconds(4567)); 34 | } 35 | 36 | TEST_CASE("Ctor throws on nullptr transport", "[InfluxDBTest]") 37 | { 38 | CHECK_THROWS_AS(InfluxDB{nullptr}, InfluxDBException); 39 | } 40 | 41 | TEST_CASE("Write transmits point", "[InfluxDBTest]") 42 | { 43 | auto mock = std::make_shared(); 44 | REQUIRE_CALL(*mock, send("p f0=71i 4567000000")); 45 | 46 | InfluxDB db{std::make_unique(mock)}; 47 | db.write(Point{"p"}.addField("f0", 71).setTimestamp(ignoreTimestamp)); 48 | } 49 | 50 | TEST_CASE("Write transmits points", "[InfluxDBTest]") 51 | { 52 | auto mock = std::make_shared(); 53 | REQUIRE_CALL(*mock, send("p0 f0=0i 4567000000\np1 f1=1i 4567000000\np2 f2=2i 4567000000")); 54 | 55 | InfluxDB db{std::make_unique(mock)}; 56 | db.write({Point{"p0"}.addField("f0", 0).setTimestamp(ignoreTimestamp), 57 | Point{"p1"}.addField("f1", 1).setTimestamp(ignoreTimestamp), 58 | Point{"p2"}.addField("f2", 2).setTimestamp(ignoreTimestamp)}); 59 | } 60 | 61 | TEST_CASE("Write adds global tags", "[InfluxDBTest]") 62 | { 63 | auto mock = std::make_shared(); 64 | REQUIRE_CALL(*mock, send(R"(p0,x=1,t\,\=\ =v\,\=\ f0=11i 4567000000)" 65 | "\n" 66 | R"(p1,x=1,t\,\=\ =v\,\=\ ,existing=yes f1=22i 4567000000)" 67 | "\n" 68 | R"(p2,x=1,t\,\=\ =v\,\=\ f2=33i 4567000000)")); 69 | REQUIRE_CALL(*mock, send(R"(p4,x=1,t\,\=\ =v\,\=\ f3=44i 4567000000)")); 70 | REQUIRE_CALL(*mock, send(R"(p5,x=1,t\,\=\ =v\,\=\ f4=55i 4567000000)")); 71 | 72 | InfluxDB db{std::make_unique(mock)}; 73 | db.addGlobalTag("x", "1"); 74 | // Special characters in global tags and values should be escaped 75 | db.addGlobalTag("t,= ", "v,= "); 76 | db.write({Point{"p0"}.addField("f0", 11).setTimestamp(ignoreTimestamp), 77 | Point{"p1"}.addField("f1", 22).addTag("existing", "yes").setTimestamp(ignoreTimestamp), 78 | Point{"p2"}.addField("f2", 33).setTimestamp(ignoreTimestamp)}); 79 | db.write(Point{"p4"}.addField("f3", 44).setTimestamp(ignoreTimestamp)); 80 | db.batchOf(1); 81 | db.write(Point{"p5"}.addField("f4", 55).setTimestamp(ignoreTimestamp)); 82 | } 83 | 84 | TEST_CASE("Write with batch enabled adds point to batch if size not reached", "[InfluxDBTest]") 85 | { 86 | using trompeloeil::_; 87 | 88 | auto mock = std::make_shared(); 89 | 90 | InfluxDB db{std::make_unique(mock)}; 91 | db.batchOf(3); 92 | db.write(Point{"x"}); 93 | 94 | ALLOW_CALL(*mock, send(_)); 95 | db.flushBatch(); 96 | } 97 | 98 | TEST_CASE("Write with batch enabled writes points if size reached", "[InfluxDBTest]") 99 | { 100 | using trompeloeil::_; 101 | 102 | auto mock = std::make_shared(); 103 | REQUIRE_CALL(*mock, send("x 4567000000\ny 4567000000\nz 4567000000")); 104 | 105 | InfluxDB db{std::make_unique(mock)}; 106 | db.batchOf(3); 107 | db.write(Point{"x"}.setTimestamp(ignoreTimestamp)); 108 | db.write({Point{"y"}.setTimestamp(ignoreTimestamp), 109 | Point{"z"}.setTimestamp(ignoreTimestamp), 110 | Point{"not-transmitted"}.setTimestamp(ignoreTimestamp)}); 111 | 112 | ALLOW_CALL(*mock, send(_)); 113 | db.flushBatch(); 114 | } 115 | 116 | TEST_CASE("Flush batch transmits pending points", "[InfluxDBTest]") 117 | { 118 | using trompeloeil::_; 119 | 120 | auto mock = std::make_shared(); 121 | 122 | InfluxDB db{std::make_unique(mock)}; 123 | db.batchOf(300); 124 | db.write(Point{"x"}.setTimestamp(ignoreTimestamp)); 125 | db.write({Point{"y"}.setTimestamp(ignoreTimestamp), 126 | Point{"z"}.setTimestamp(ignoreTimestamp)}); 127 | 128 | REQUIRE_CALL(*mock, send("x 4567000000\ny 4567000000\nz 4567000000")); 129 | db.flushBatch(); 130 | } 131 | 132 | TEST_CASE("Destructs cleanly with pending batches", "[InfluxDBTest]") 133 | { 134 | using trompeloeil::_; 135 | 136 | auto mock = std::make_shared(); 137 | 138 | { 139 | ALLOW_CALL(*mock, send(_)).THROW(std::runtime_error{"Intentional"}); 140 | InfluxDB db{std::make_unique(mock)}; 141 | db.batchOf(100); 142 | db.write(Point{"x"}.setTimestamp(ignoreTimestamp)); 143 | } 144 | } 145 | 146 | TEST_CASE("Flush batch does nothing if batch disabled", "[InfluxDBTest]") 147 | { 148 | auto mock = std::make_shared(); 149 | InfluxDB db{std::make_unique(mock)}; 150 | 151 | { 152 | REQUIRE_CALL(*mock, send("x 4567000000")); 153 | db.write(Point{"x"}.setTimestamp(ignoreTimestamp)); 154 | } 155 | 156 | db.flushBatch(); 157 | } 158 | 159 | TEST_CASE("Clear Batch clears batch", "[InfluxDBTest]") 160 | { 161 | auto mock = std::make_shared(); 162 | InfluxDB db{std::make_unique(mock)}; 163 | db.batchOf(10); 164 | 165 | db.write(Point{"x"}.setTimestamp(ignoreTimestamp)); 166 | CHECK(db.batchSize() == 1); 167 | db.clearBatch(); 168 | CHECK(db.batchSize() == 0); 169 | } 170 | 171 | TEST_CASE("Create database throws if unsupported by transport", "[InfluxDBTest]") 172 | { 173 | auto mock = std::make_shared(); 174 | REQUIRE_CALL(*mock, createDatabase()).THROW(InfluxDBException{"Intentional"}); 175 | 176 | InfluxDB db{std::make_unique(mock)}; 177 | CHECK_THROWS_AS(db.createDatabaseIfNotExists(), InfluxDBException); 178 | } 179 | 180 | TEST_CASE("Create database creates database through transport", "[InfluxDBTest]") 181 | { 182 | auto mock = std::make_shared(); 183 | REQUIRE_CALL(*mock, createDatabase()); 184 | 185 | InfluxDB db{std::make_unique(mock)}; 186 | db.createDatabaseIfNotExists(); 187 | } 188 | 189 | TEST_CASE("Execute executes query", "[InfluxDBTest]") 190 | { 191 | const std::string response = "name: databases\nname\n----\n_internal\n"; 192 | auto mock = std::make_shared(); 193 | REQUIRE_CALL(*mock, execute("show databases")).RETURN(response); 194 | 195 | InfluxDB db{std::make_unique(mock)}; 196 | const auto result = db.execute("show databases"); 197 | CHECK(result == response); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /test/LineProtocolTest.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "LineProtocol.h" 24 | #include 25 | #include 26 | #include 27 | 28 | namespace influxdb::test 29 | { 30 | using namespace Catch::Matchers; 31 | 32 | namespace 33 | { 34 | constexpr std::chrono::time_point ignoreTimestamp(std::chrono::milliseconds(54)); 35 | } 36 | 37 | 38 | TEST_CASE("Empty measurement", "[LineProtocolTest]") 39 | { 40 | const auto point = Point{"p0"}.setTimestamp(ignoreTimestamp); 41 | const LineProtocol lineProtocol; 42 | CHECK_THAT(lineProtocol.format(point), Equals("p0 54000000")); 43 | } 44 | 45 | TEST_CASE("Measurement with value", "[LineProtocolTest]") 46 | { 47 | const auto point = Point{"p0"}.addField("f0", "1").setTimestamp(ignoreTimestamp); 48 | const LineProtocol lineProtocol; 49 | CHECK_THAT(lineProtocol.format(point), Equals(R"(p0 f0="1" 54000000)")); 50 | } 51 | 52 | TEST_CASE("Measurement with different types", "[LineProtocolTest]") 53 | { 54 | const auto point = Point{"multitype"} 55 | .addField("int_value", 567) 56 | .addField("longlong_value", 1234567890LL) 57 | .addField("double_value", 123.4567) 58 | .addField("string_value", "abc def ghi") 59 | .addField("bool_true_field", true) 60 | .addField("bool_false_field", false) 61 | .addField("uint_field", std::numeric_limits::max()) 62 | .addField("ulonglong_field", std::numeric_limits::max()) 63 | .setTimestamp(ignoreTimestamp); 64 | 65 | const LineProtocol lineProtocol; 66 | CHECK_THAT(lineProtocol.format(point), Matches("multitype " 67 | "int_value=567i," 68 | "longlong_value=1234567890i," 69 | "double_value=123.45[0-9]*," 70 | "string_value=\"abc def ghi\"," 71 | "bool_true_field=true," 72 | "bool_false_field=false," 73 | "uint_field=4294967295u," 74 | "ulonglong_field=18446744073709551615u" 75 | " 54000000")); 76 | } 77 | 78 | TEST_CASE("Measurement with multiple values", "[LineProtocolTest]") 79 | { 80 | const auto point = Point{"multiFieldPoint"} 81 | .addField("value0", 4455) 82 | .addField("value1", 99807) 83 | .addField("value2", 2334) 84 | .setTimestamp(ignoreTimestamp); 85 | const LineProtocol lineProtocol; 86 | CHECK_THAT(lineProtocol.format(point), Equals("multiFieldPoint value0=4455i," 87 | "value1=99807i," 88 | "value2=2334i" 89 | " 54000000")); 90 | } 91 | 92 | TEST_CASE("Measurement with tag", "[LineProtocolTest]") 93 | { 94 | const auto point = Point{"taggedPoint"} 95 | .addField("x", 5) 96 | .addTag("tag0", "value0") 97 | .setTimestamp(ignoreTimestamp); 98 | const LineProtocol lineProtocol; 99 | CHECK_THAT(lineProtocol.format(point), Equals(R"(taggedPoint,tag0=value0 x=5i 54000000)")); 100 | } 101 | 102 | TEST_CASE("Measurement with multiple tags", "[LineProtocolTest]") 103 | { 104 | const auto point = Point{"taggedPoint"} 105 | .addField("y", 9) 106 | .addTag("t0", "v0") 107 | .addTag("t1", "v1") 108 | .addTag("t2", "v2") 109 | .setTimestamp(ignoreTimestamp); 110 | const LineProtocol lineProtocol; 111 | CHECK_THAT(lineProtocol.format(point), Equals(R"(taggedPoint,t0=v0,t1=v1,t2=v2 y=9i 54000000)")); 112 | } 113 | 114 | TEST_CASE("Adds global tag", "[LineProtocolTest]") 115 | { 116 | const auto point = Point{"p0"} 117 | .addField("n", 0) 118 | .setTimestamp(ignoreTimestamp); 119 | const LineProtocol lineProtocol{"global=true"}; 120 | CHECK_THAT(lineProtocol.format(point), Equals(R"(p0,global=true n=0i 54000000)")); 121 | } 122 | 123 | TEST_CASE("Adds global tag to existing tags", "[LineProtocolTest]") 124 | { 125 | const auto point = Point{"p0"} 126 | .addField("n", 0) 127 | .addTag("local", "1") 128 | .setTimestamp(ignoreTimestamp); 129 | const LineProtocol lineProtocol{"global=true"}; 130 | CHECK_THAT(lineProtocol.format(point), Equals(R"(p0,global=true,local=1 n=0i 54000000)")); 131 | } 132 | 133 | TEST_CASE("Adds multiple global tags", "[LineProtocolTest]") 134 | { 135 | const auto point = Point{"p1"} 136 | .addField("n", 1) 137 | .addTag("pointtag", "3") 138 | .setTimestamp(ignoreTimestamp); 139 | const LineProtocol lineProtocol{"a=0,b=1,c=2"}; 140 | CHECK_THAT(lineProtocol.format(point), Equals(R"(p1,a=0,b=1,c=2,pointtag=3 n=1i 54000000)")); 141 | } 142 | 143 | TEST_CASE("Escapes Measurement string element", "[LineProtocolTest]") 144 | { 145 | // Measurement must escape comma and space characters 146 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::Measurement, "no_escape"), 147 | Equals("no_escape")); 148 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::Measurement, "escape space"), 149 | Equals(R"(escape\ space)")); 150 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::Measurement, "escape,comma"), 151 | Equals(R"(escape\,comma)")); 152 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::Measurement, "escape, multiple"), 153 | Equals(R"(escape\,\ multiple)")); 154 | } 155 | 156 | TEST_CASE("Escapes Tag key string element", "[LineProtocolTest]") 157 | { 158 | // Tag key must escape comma, equals sign and space characters 159 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::TagKey, "no_escape"), 160 | Equals("no_escape")); 161 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::TagKey, "escape,comma"), 162 | Equals(R"(escape\,comma)")); 163 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::TagKey, "escape space"), 164 | Equals(R"(escape\ space)")); 165 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::TagKey, "escape=equal"), 166 | Equals(R"(escape\=equal)")); 167 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::TagKey, "escape = multiple,"), 168 | Equals(R"(escape\ \=\ multiple\,)")); 169 | } 170 | 171 | TEST_CASE("Escapes Tag value string element", "[LineProtocolTest]") 172 | { 173 | // Tag value must escape comma, equals sign and space characters 174 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::TagValue, "no_escape"), 175 | Equals("no_escape")); 176 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::TagValue, "escape,comma"), 177 | Equals(R"(escape\,comma)")); 178 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::TagValue, "escape space"), 179 | Equals(R"(escape\ space)")); 180 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::TagValue, "escape=equal"), 181 | Equals(R"(escape\=equal)")); 182 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::TagValue, "escape = multiple,"), 183 | Equals(R"(escape\ \=\ multiple\,)")); 184 | } 185 | 186 | TEST_CASE("Escapes Field key string element", "[LineProtocolTest]") 187 | { 188 | // Field key must escape comma, equals sign and space characters 189 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::FieldKey, "no_escape"), 190 | Equals("no_escape")); 191 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::FieldKey, "escape,comma"), 192 | Equals(R"(escape\,comma)")); 193 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::FieldKey, "escape space"), 194 | Equals(R"(escape\ space)")); 195 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::FieldKey, "escape=equal"), 196 | Equals(R"(escape\=equal)")); 197 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::FieldKey, "escape = multiple,"), 198 | Equals(R"(escape\ \=\ multiple\,)")); 199 | } 200 | 201 | TEST_CASE("Escapes Field value string element", "[LineProtocolTest]") 202 | { 203 | // Field value must escape double quote and backslash characters 204 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::FieldValue, "no_escape"), 205 | Equals("no_escape")); 206 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::FieldValue, R"(escape"quote)"), 207 | Equals(R"(escape\"quote)")); 208 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::FieldValue, R"(escape\backslash)"), 209 | Equals(R"(escape\\backslash)")); 210 | CHECK_THAT(LineProtocol::EscapeStringElement(LineProtocol::ElementType::FieldValue, R"(escape\"both)"), 211 | Equals(R"(escape\\\"both)")); 212 | } 213 | 214 | TEST_CASE("Escapes all element types", "[LineProtocolTest]") 215 | { 216 | const auto point = Point{"measurement, "} 217 | .addTag("tag,= key", "tag,= value") 218 | .addField("field,= key", R"("field\value")") 219 | .setTimestamp(ignoreTimestamp); 220 | const LineProtocol lineProtocol{}; 221 | const std::string expected{R"(measurement\,\ ,tag\,\=\ key=tag\,\=\ value field\,\=\ key="\"field\\value\"" 54000000)"}; 222 | CHECK_THAT(lineProtocol.format(point), Equals(expected)); 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /test/NoBoostSupportTest.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "BoostSupport.h" 24 | #include "InfluxDB/InfluxDBException.h" 25 | #include 26 | 27 | namespace influxdb::test 28 | { 29 | namespace 30 | { 31 | struct TransportDummy : public Transport 32 | { 33 | void send([[maybe_unused]] std::string&& message) override 34 | { 35 | } 36 | }; 37 | 38 | TransportDummy dummy; 39 | } 40 | 41 | 42 | TEST_CASE("Query impl throws unconditionally", "[NoBoostSupportTest]") 43 | { 44 | CHECK_THROWS_AS(internal::queryImpl(&dummy, "-ignore-"), InfluxDBException); 45 | } 46 | 47 | TEST_CASE("With UDP throws transport unconditionally", "[NoBoostSupportTest]") 48 | { 49 | CHECK_THROWS_AS(internal::withUdpTransport(http::url{}), InfluxDBException); 50 | } 51 | 52 | TEST_CASE("With Unix socket transport throws unconditionally", "[NoBoostSupportTest]") 53 | { 54 | CHECK_THROWS_AS(internal::withUnixSocketTransport(http::url{}), InfluxDBException); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /test/PointTest.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "InfluxDB/Point.h" 24 | #include 25 | #include 26 | #include 27 | 28 | namespace influxdb::test 29 | { 30 | using namespace Catch::Matchers; 31 | 32 | TEST_CASE("Empty measurement", "[PointTest]") 33 | { 34 | const Point point{"test"}; 35 | CHECK_THAT(point.getName(), Equals("test")); 36 | CHECK_THAT(point.getFields(), Equals("")); 37 | CHECK_THAT(point.getTags(), Equals("")); 38 | } 39 | 40 | TEST_CASE("Measurement with value", "[PointTest]") 41 | { 42 | const auto point = Point{"test"}.addField("field_name", "field_value"); 43 | CHECK_THAT(point.getFields(), Equals(R"(field_name="field_value")")); 44 | } 45 | 46 | TEST_CASE("Measurement with values of different types", "[PointTest]") 47 | { 48 | const auto point = Point{"test"} 49 | .addField("int_field", 3) 50 | .addField("longlong_field", 1234LL) 51 | .addField("string_field", "string value") 52 | .addField("double_field", double{3.859}) 53 | .addField("bool_true_field", true) 54 | .addField("bool_false_field", false) 55 | .addField("uint_field", std::numeric_limits::max()) 56 | .addField("ulonglong_field", std::numeric_limits::max()); 57 | 58 | // Set float precision to default for this test to ensure the expected double field value 59 | Point::floatsPrecision = defaultFloatsPrecision; 60 | CHECK_THAT(point.getFields(), Equals("int_field=3i," 61 | "longlong_field=1234i," 62 | "string_field=\"string value\"," 63 | "double_field=3.858999999999999986," 64 | "bool_true_field=true," 65 | "bool_false_field=false," 66 | "uint_field=4294967295u," 67 | "ulonglong_field=18446744073709551615u")); 68 | } 69 | 70 | TEST_CASE("Measurement of signed int type", "[PointTest]") 71 | { 72 | const auto point = Point{"test"} 73 | .addField("int_f", static_cast(-123)) 74 | .addField("int0_f", static_cast(0)) 75 | .addField("longlongint_f", static_cast(-1234567890LL)); 76 | CHECK_THAT(point.getFields(), Equals(R"(int_f=-123i,int0_f=0i,longlongint_f=-1234567890i)")); 77 | } 78 | 79 | TEST_CASE("Measurement of unsigned int type", "[PointTest]") 80 | { 81 | const auto point = Point{"test"} 82 | .addField("uint_f", static_cast(321)) 83 | .addField("ulonglongint_f", static_cast(1234567890LL)); 84 | CHECK_THAT(point.getFields(), Equals(R"(uint_f=321u,ulonglongint_f=1234567890u)")); 85 | } 86 | 87 | TEST_CASE("Measurement of double type", "[PointTest]") 88 | { 89 | const auto point = Point{"test"} 90 | .addField("double_f", double{-456.78934345}); 91 | CHECK_THAT(point.getFields(), StartsWith(R"(double_f=-456.78)")); 92 | } 93 | 94 | TEST_CASE("Measurement of bool type", "[PointTest]") 95 | { 96 | const auto point = Point{"test"} 97 | .addField("bool_f", true); 98 | CHECK_THAT(point.getFields(), Equals(R"(bool_f=true)")); 99 | } 100 | 101 | TEST_CASE("Measurement of string type", "[PointTest]") 102 | { 103 | const auto point = Point{"test"} 104 | .addField("stdstring_f", std::string{"aAa bBB Ccc"}) 105 | .addField("cstr_f", "dD Ee"); 106 | CHECK_THAT(point.getFields(), Equals(R"(stdstring_f="aAa bBB Ccc",cstr_f="dD Ee")")); 107 | } 108 | 109 | TEST_CASE("Field with empty name is not added", "[PointTest]") 110 | { 111 | const auto point = Point{"test"}.addField("", "not added"); 112 | CHECK_THAT(point.getFields(), Equals("")); 113 | } 114 | 115 | TEST_CASE("Field with empty value is added", "[PointTest]") 116 | { 117 | const auto point = Point{"test"}.addField("added", ""); 118 | CHECK_THAT(point.getFields(), Equals("added=\"\"")); 119 | } 120 | 121 | TEST_CASE("Measurement with tag", "[PointTest]") 122 | { 123 | const auto point = Point{"test"}.addTag("tag_name", "tag_value"); 124 | CHECK_THAT(point.getTags(), Equals("tag_name=tag_value")); 125 | } 126 | 127 | TEST_CASE("Measurement with multiple tags", "[PointTest]") 128 | { 129 | const auto point = Point{"test"} 130 | .addTag("tag_0", "value_0") 131 | .addTag("tag_1", "value_1") 132 | .addTag("tag_2", "value_2"); 133 | CHECK_THAT(point.getTags(), Equals("tag_0=value_0,tag_1=value_1,tag_2=value_2")); 134 | } 135 | 136 | TEST_CASE("Empty tag value is not added", "[PointTest]") 137 | { 138 | const auto point = Point{"test"}.addTag("tag", ""); 139 | CHECK_THAT(point.getTags(), Equals("")); 140 | } 141 | 142 | TEST_CASE("Empty tag key is not added", "[PointTest]") 143 | { 144 | const auto point = Point{"test"}.addTag("", "value"); 145 | CHECK_THAT(point.getTags(), Equals("")); 146 | } 147 | 148 | TEST_CASE("Measurement with specific time stamp", "[PointTest]") 149 | { 150 | const std::chrono::time_point timeStamp{std::chrono::milliseconds{1572830915}}; 151 | const auto point = Point{"test"}.setTimestamp(timeStamp); 152 | CHECK(point.getTimestamp() == timeStamp); 153 | } 154 | 155 | TEST_CASE("Float field precision can be adjusted", "[PointTest]") 156 | { 157 | Point::floatsPrecision = 3; 158 | const auto pointPrecision3 = Point{"test"}.addField("float_field", 3.123456789); 159 | CHECK_THAT(pointPrecision3.getFields(), Equals("float_field=3.123")); 160 | 161 | Point::floatsPrecision = 1; 162 | const auto pointPrecision1 = Point{"test"}.addField("float_field", 50.123456789); 163 | CHECK_THAT(pointPrecision1.getFields(), Equals("float_field=50.1")); 164 | 165 | const auto secondPointPrecision1 = Point{"test"}.addField("float_field", 5.99); 166 | CHECK_THAT(secondPointPrecision1.getFields(), Equals("float_field=6.0")); 167 | } 168 | 169 | TEST_CASE("Float field precision with scientific values", "[PointTest]") 170 | { 171 | Point::floatsPrecision = 5; 172 | const auto point0 = Point{"test"}.addField("float_field", 123456789.0); 173 | CHECK_THAT(point0.getFields(), Equals("float_field=123456789.00000")); 174 | 175 | const auto point1 = Point{"test"}.addField("float_field", 1.23456789E+8); 176 | CHECK_THAT(point1.getFields(), Equals("float_field=123456789.00000")); 177 | 178 | const auto point2 = Point{"test"}.addField("float_field", 1.23456789E-3); 179 | CHECK_THAT(point2.getFields(), Equals("float_field=0.00123")); 180 | 181 | const auto point3 = Point{"test"}.addField("float_field", 1.23456789E-6); 182 | CHECK_THAT(point3.getFields(), Equals("float_field=0.00000")); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /test/ProxyTest.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "InfluxDB/Proxy.h" 24 | #include 25 | #include 26 | 27 | 28 | namespace influxdb::test 29 | { 30 | using namespace Catch::Matchers; 31 | 32 | TEST_CASE("Proxy without authentication", "[ProxyTest]") 33 | { 34 | const Proxy p("https://proxy-host"); 35 | CHECK_THAT(p.getProxy(), Equals("https://proxy-host")); 36 | CHECK(p.getAuthentication().has_value() == false); 37 | } 38 | 39 | TEST_CASE("Proxy with authentication", "[ProxyTest]") 40 | { 41 | const Proxy p("https://proxy-host", Proxy::Auth{"uname", "upw"}); 42 | CHECK_THAT(p.getProxy(), Equals("https://proxy-host")); 43 | 44 | const auto auth = p.getAuthentication(); 45 | CHECK(auth.has_value() == true); 46 | CHECK_THAT(auth->user, Equals("uname")); 47 | CHECK_THAT(auth->password, Equals("upw")); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /test/UriParserTest.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "UriParser.h" 24 | #include 25 | #include 26 | 27 | namespace influxdb::test 28 | { 29 | namespace 30 | { 31 | http::url parse(std::string in) 32 | { 33 | return http::ParseHttpUrl(in); 34 | } 35 | } 36 | 37 | 38 | TEST_CASE("Parse url", "[UriParserTest]") 39 | { 40 | const std::string input{"https://xyz.com/fghi/jklm"}; 41 | const auto url = parse(input); 42 | 43 | CHECK(url.protocol == "https"); 44 | CHECK(url.user == ""); 45 | CHECK(url.password == ""); 46 | CHECK(url.host == "xyz.com"); 47 | CHECK(url.path == "/fghi/jklm"); 48 | CHECK(url.search == ""); 49 | CHECK(url.url == input); 50 | CHECK(url.port == 0); 51 | } 52 | 53 | TEST_CASE("Parse protocol", "[UriParserTest]") 54 | { 55 | CHECK(parse("http://xyz.com").protocol == "http"); 56 | CHECK(parse("https://xyz.com").protocol == "https"); 57 | CHECK(parse("udp://xyz.com").protocol == "udp"); 58 | CHECK(parse("unix://xyz.com").protocol == "unix"); 59 | } 60 | 61 | TEST_CASE("Parse param", "[UriParserTest]") 62 | { 63 | CHECK(parse("http://xyz.com/aaa?param=value").search == "param=value"); 64 | } 65 | 66 | TEST_CASE("Parse port", "[UriParserTest]") 67 | { 68 | CHECK(parse("http://xyz.com:12345").port == 12345); 69 | } 70 | 71 | TEST_CASE("Parse basic auth", "[UriParserTest]") 72 | { 73 | const auto url = parse("https://aaa:bbb@host0"); 74 | CHECK(url.user == "aaa"); 75 | CHECK(url.password == "bbb"); 76 | } 77 | 78 | TEST_CASE("Parse auth token", "[UriParserTest]") 79 | { 80 | CHECK(parse("http://token@xyz.com").password == "token"); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /test/mock/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(CprMock STATIC CprMock.cxx) 2 | target_include_directories(CprMock PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 3 | target_link_libraries(CprMock PRIVATE Catch2::Catch2 trompeloeil::trompeloeil) 4 | target_include_directories(CprMock SYSTEM PUBLIC $) 5 | -------------------------------------------------------------------------------- /test/mock/CprMock.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "CprMock.h" 24 | #include 25 | 26 | namespace cpr 27 | { 28 | namespace 29 | { 30 | class ParametersSpy : public Parameters 31 | { 32 | public: 33 | static std::map toMap(const Parameters& p) 34 | { 35 | const auto& entries = static_cast(p).containerList_; 36 | std::map params{}; 37 | 38 | std::transform(entries.cbegin(), entries.cend(), std::inserter(params, params.end()), [](const auto& e) 39 | { return std::pair{e.key, e.value}; }); 40 | return params; 41 | } 42 | }; 43 | } 44 | 45 | 46 | template 47 | CurlContainer::CurlContainer(const std::initializer_list& containerList) 48 | : containerList_(containerList) 49 | { 50 | } 51 | 52 | Session::Session() = default; 53 | 54 | void Session::SetTimeout(const Timeout& timeout) 55 | { 56 | influxdb::test::sessionMock.SetTimeout(timeout); 57 | } 58 | 59 | void Session::SetConnectTimeout(const ConnectTimeout& timeout) 60 | { 61 | influxdb::test::sessionMock.SetConnectTimeout(timeout); 62 | } 63 | 64 | void Session::SetBody(Body&& body) 65 | { 66 | influxdb::test::sessionMock.SetBody(std::move(body)); 67 | } 68 | 69 | Response Session::Get() 70 | { 71 | return influxdb::test::sessionMock.Get(); 72 | } 73 | Response Session::Post() 74 | { 75 | return influxdb::test::sessionMock.Post(); 76 | } 77 | 78 | void Session::SetAuth(const Authentication& auth) 79 | { 80 | influxdb::test::sessionMock.SetAuth(auth); 81 | } 82 | 83 | void Session::SetProxyAuth(ProxyAuthentication&& proxy_auth) 84 | { 85 | influxdb::test::sessionMock.SetProxyAuth(std::move(proxy_auth)); 86 | } 87 | 88 | void Session::SetVerifySsl(const VerifySsl& verify) 89 | { 90 | influxdb::test::sessionMock.SetVerifySsl(verify); 91 | } 92 | 93 | void Session::SetParameters(Parameters&& parameters) 94 | { 95 | influxdb::test::sessionMock.SetParameters(ParametersSpy::toMap(parameters)); 96 | } 97 | 98 | void Session::SetProxies(Proxies&& proxies) 99 | { 100 | influxdb::test::sessionMock.SetProxies(std::move(proxies)); 101 | } 102 | 103 | void Session::SetUrl(const Url& url) 104 | { 105 | influxdb::test::sessionMock.SetUrl(url); 106 | } 107 | 108 | void Session::SetHeader(const Header& header) 109 | { 110 | influxdb::test::sessionMock.SetHeader(header); 111 | } 112 | 113 | void Session::UpdateHeader(const Header& header) 114 | { 115 | influxdb::test::sessionMock.UpdateHeader(header); 116 | } 117 | 118 | Proxies::Proxies(const std::initializer_list>& hosts) 119 | : hosts_{hosts} 120 | { 121 | } 122 | 123 | bool CaseInsensitiveCompare::operator()([[maybe_unused]] const std::string& a, [[maybe_unused]] const std::string& b) const noexcept 124 | { 125 | return false; 126 | } 127 | 128 | const std::string& Proxies::operator[](const std::string& protocol) 129 | { 130 | if (hosts_.count(protocol) == 0) 131 | { 132 | FAIL("Proxies: No entry '" << protocol << "' available"); 133 | } 134 | return hosts_[protocol]; 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /test/mock/CprMock.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | namespace influxdb::test 31 | { 32 | 33 | class SessionMock 34 | { 35 | public: 36 | MAKE_MOCK1(SetTimeout, void(const cpr::Timeout&)); 37 | MAKE_MOCK1(SetConnectTimeout, void(const cpr::ConnectTimeout&)); 38 | MAKE_MOCK0(Get, cpr::Response()); 39 | MAKE_MOCK0(Post, cpr::Response()); 40 | MAKE_MOCK1(SetUrl, void(const cpr::Url&)); 41 | MAKE_MOCK1(SetHeader, void(const cpr::Header&)); 42 | MAKE_MOCK1(UpdateHeader, void(const cpr::Header&)); 43 | MAKE_MOCK1(SetBody, void(cpr::Body&&)); 44 | MAKE_MOCK1(SetParameters, void(std::map)); 45 | MAKE_MOCK1(SetAuth, void(const cpr::Authentication&)); 46 | MAKE_MOCK1(SetProxies, void(cpr::Proxies&&)); 47 | MAKE_MOCK1(SetProxyAuth, void(cpr::ProxyAuthentication&&)); 48 | MAKE_MOCK1(SetVerifySsl, void(const cpr::VerifySsl&)); 49 | }; 50 | 51 | extern SessionMock sessionMock; 52 | } 53 | -------------------------------------------------------------------------------- /test/mock/TransportMock.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #include "InfluxDB/Transport.h" 26 | #include 27 | #include 28 | 29 | namespace influxdb::test 30 | { 31 | class TransportMock : public Transport 32 | { 33 | MAKE_MOCK1(send, void(std::string&&), override); 34 | MAKE_MOCK1(query, std::string(const std::string&), override); 35 | MAKE_MOCK0(createDatabase, void(), override); 36 | MAKE_MOCK1(execute, std::string(const std::string&), override); 37 | }; 38 | 39 | 40 | class TransportAdapter : public Transport 41 | { 42 | public: 43 | explicit TransportAdapter(std::shared_ptr mock) 44 | : mockImpl(mock) 45 | { 46 | } 47 | 48 | void send(std::string&& message) override 49 | { 50 | mockImpl->send(std::move(message)); 51 | } 52 | 53 | std::string query(const std::string& query) override 54 | { 55 | return mockImpl->query(query); 56 | } 57 | 58 | std::string execute(const std::string& cmd) override 59 | { 60 | return mockImpl->execute(cmd); 61 | } 62 | 63 | void createDatabase() override 64 | { 65 | mockImpl->createDatabase(); 66 | } 67 | 68 | private: 69 | std::shared_ptr mockImpl; 70 | }; 71 | 72 | } 73 | -------------------------------------------------------------------------------- /test/system/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(InfluxDBST InfluxDBST.cxx) 2 | target_link_libraries(InfluxDBST PRIVATE InfluxDB Catch2::Catch2WithMain) 3 | 4 | add_executable(HttpAuthST HttpAuthST.cxx) 5 | target_link_libraries(HttpAuthST PRIVATE InfluxDB Catch2::Catch2WithMain) 6 | 7 | 8 | add_custom_target(systemtest InfluxDBST 9 | COMMENT "Running system tests\n\n" 10 | VERBATIM 11 | ) 12 | 13 | add_custom_target(systemtest-auth HttpAuthST 14 | COMMENT "Running system tests with authentication\n\n" 15 | VERBATIM 16 | ) 17 | 18 | -------------------------------------------------------------------------------- /test/system/HttpAuthST.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "SystemTest.h" 24 | #include "InfluxDB/InfluxDBBuilder.h" 25 | 26 | namespace influxdb::test 27 | { 28 | TEST_CASE("Http authentication test", "[HttpAuthTest]") 29 | { 30 | using Catch::Matchers::ContainsSubstring; 31 | 32 | SECTION("Unauthenticated users fails") 33 | { 34 | auto db = configure("st_auth_db", {}); 35 | CHECK_THROWS_AS(db->execute("show users"), InfluxDBException); 36 | } 37 | 38 | SECTION("Authenticated user has access") 39 | { 40 | const auto user = getUserFromEnv(); 41 | auto db = configure("st_auth_db", user); 42 | 43 | const auto response = db->execute("show users"); 44 | CHECK_THAT(response, ContainsSubstring(user.name)); 45 | } 46 | } 47 | 48 | TEST_CASE("Http authentication methods", "[HttpAuthTest]") 49 | { 50 | using Catch::Matchers::ContainsSubstring; 51 | 52 | const auto user = getUserFromEnv(); 53 | 54 | SECTION("Authenticate through factory") 55 | { 56 | auto db = InfluxDBFactory::Get("http://" + (user.name + ":" + user.pass + "@") + getHostFromEnv() + ":8086?db=ignore"); 57 | 58 | const auto response = db->execute("show users"); 59 | CHECK_THAT(response, ContainsSubstring(user.name)); 60 | } 61 | 62 | SECTION("Authenticate through builder") 63 | { 64 | auto db = InfluxDBBuilder::http("http://" + getHostFromEnv() + ":8086?db=ignore") 65 | .setBasicAuthentication(user.name, user.pass) 66 | .connect(); 67 | 68 | const auto response = db->execute("show users"); 69 | CHECK_THAT(response, ContainsSubstring(user.name)); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/system/InfluxDBST.cxx: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "SystemTest.h" 24 | #include "InfluxDB/InfluxDBBuilder.h" 25 | 26 | namespace influxdb::test 27 | { 28 | namespace 29 | { 30 | std::size_t querySize(influxdb::InfluxDB& db, const std::string& measurement, const std::string& tag) 31 | { 32 | // Quote the measurement name to avoid escaped characters being incorrectly interpreted 33 | return db.query(R"(select * from ")" + measurement + R"(" where type=')" + tag + "'").size(); 34 | } 35 | 36 | std::size_t querySize(influxdb::InfluxDB& db, const std::string& tag) 37 | { 38 | return querySize(db, "x", tag); 39 | } 40 | } 41 | 42 | TEST_CASE("Connection configuration", "[InfluxDBST]") 43 | { 44 | using namespace Catch::Matchers; 45 | 46 | SECTION("Connect through factory") 47 | { 48 | auto db = InfluxDBFactory::Get("http://" + getHostFromEnv() + ":8086?db=ignore"); 49 | const auto response = db->execute("show databases"); 50 | CHECK(response.empty() == false); 51 | } 52 | 53 | SECTION("Connect through builder") 54 | { 55 | auto db = InfluxDBBuilder::http("http://" + getHostFromEnv() + ":8086?db=ignore") 56 | .setTimeout(std::chrono::seconds{5}) 57 | .connect(); 58 | const auto response = db->execute("show databases"); 59 | CHECK(response.empty() == false); 60 | } 61 | } 62 | 63 | TEST_CASE("InfluxDB system test", "[InfluxDBST]") 64 | { 65 | using namespace Catch::Matchers; 66 | 67 | auto db = configure("st_db"); 68 | 69 | 70 | SECTION("Database does not exist") 71 | { 72 | const auto response = db->execute("show databases"); 73 | CHECK_THAT(response, !ContainsSubstring(R"(["st_db"])")); 74 | } 75 | 76 | SECTION("Query on non existing database returns empty") 77 | { 78 | CHECK(db->query("select * from st_db").empty()); 79 | } 80 | 81 | SECTION("Create database if not existing") 82 | { 83 | db->createDatabaseIfNotExists(); 84 | CHECK_THAT(db->execute("show databases"), ContainsSubstring(R"(["st_db"])")); 85 | } 86 | 87 | SECTION("Create database if not existing does nothing on existing database") 88 | { 89 | db->createDatabaseIfNotExists(); 90 | } 91 | 92 | SECTION("Created database is empty") 93 | { 94 | CHECK(db->query("select * from st_db").empty()); 95 | } 96 | 97 | SECTION("Write point") 98 | { 99 | CHECK(querySize(*db, "sp") == 0); 100 | db->write(Point{"x"}.addField("value", 20).addTag("type", "sp")); 101 | CHECK(querySize(*db, "sp") == 1); 102 | } 103 | 104 | SECTION("Query point") 105 | { 106 | const auto response = db->query("select * from x"); 107 | CHECK(response.size() == 1); 108 | CHECK(response[0].getName() == "x"); 109 | CHECK(response[0].getFields() == "value=20.000000000000000000"); 110 | CHECK(response[0].getTags() == "type=sp"); 111 | } 112 | 113 | SECTION("Query point with no matches") 114 | { 115 | const auto response = db->query("select * from nothing_to_find"); 116 | CHECK(response.empty()); 117 | } 118 | 119 | SECTION("Query point grouped by tag") 120 | { 121 | const auto response = db->query("select * from x group by type"); 122 | CHECK(response.size() == 1); 123 | CHECK(response[0].getTags() == "type=sp"); 124 | } 125 | 126 | SECTION("Query point throws on invalid query") 127 | { 128 | CHECK_THROWS_AS(db->query("select* from_INVALID"), InfluxDBException); 129 | } 130 | 131 | SECTION("Write multiple points") 132 | { 133 | CHECK(querySize(*db, "mp") == 0); 134 | db->write(Point{"x"}.addField("n", 0).addTag("type", "mp")); 135 | CHECK(querySize(*db, "mp") == 1); 136 | db->write(Point{"x"}.addField("n", 1).addTag("type", "mp")); 137 | CHECK(querySize(*db, "mp") == 2); 138 | db->write(Point{"x"}.addField("n", 2).addTag("type", "mp")); 139 | CHECK(querySize(*db, "mp") == 3); 140 | db->write(Point{"x"}.addField("n", 2).addTag("type", "mp")); 141 | CHECK(querySize(*db, "mp") == 4); 142 | } 143 | 144 | SECTION("Write multiple points by container") 145 | { 146 | CHECK(querySize(*db, "mpc") == 0); 147 | 148 | db->write({Point{"x"}.addField("n", 0).addTag("type", "mpc"), 149 | Point{"x"}.addField("n", 1).addTag("type", "mpc"), 150 | Point{"x"}.addField("n", 2).addTag("type", "mpc")}); 151 | 152 | CHECK(querySize(*db, "mpc") == 3); 153 | } 154 | 155 | SECTION("Query points") 156 | { 157 | const auto response = db->query(R"(select * from x where type='mpc')"); 158 | CHECK(response.size() == 3); 159 | CHECK(response[0].getName() == "x"); 160 | CHECK(response[0].getFields() == "n=0.000000000000000000"); 161 | CHECK(response[1].getName() == "x"); 162 | CHECK(response[1].getFields() == "n=1.000000000000000000"); 163 | CHECK(response[2].getName() == "x"); 164 | CHECK(response[2].getFields() == "n=2.000000000000000000"); 165 | } 166 | 167 | SECTION("Write as batch doesn't send if batch size not reached") 168 | { 169 | db->batchOf(3); 170 | 171 | CHECK(querySize(*db, "bpns") == 0); 172 | db->write({Point{"x"}.addField("n", 0).addTag("type", "bpns"), 173 | Point{"x"}.addField("n", 1).addTag("type", "bpns")}); 174 | CHECK(querySize(*db, "bpns") == 0); 175 | } 176 | 177 | SECTION("Write as batch sends if batch size reached") 178 | { 179 | db->batchOf(2); 180 | 181 | CHECK(querySize(*db, "bp") == 0); 182 | db->write({Point{"x"}.addField("n", 1).addTag("type", "bp"), 183 | Point{"x"}.addField("n", 2).addTag("type", "bp"), 184 | Point{"x"}.addField("n", -1).addTag("type", "bp")}); 185 | CHECK(querySize(*db, "bp") == 2); 186 | } 187 | 188 | SECTION("Write as batch sends if on flush") 189 | { 190 | db->batchOf(200); 191 | 192 | CHECK(querySize(*db, "bpf") == 0); 193 | db->write({Point{"x"}.addField("n", 1).addTag("type", "bpf"), 194 | Point{"x"}.addField("n", 2).addTag("type", "bpf"), 195 | Point{"x"}.addField("n", -1).addTag("type", "bpf")}); 196 | CHECK(querySize(*db, "bpf") == 0); 197 | db->flushBatch(); 198 | CHECK(querySize(*db, "bpf") == 3); 199 | } 200 | 201 | SECTION("Write of invalid line protocol throws") 202 | { 203 | CHECK_THROWS_AS(db->write(Point{"test,this=is ,,====,, invalid"}), InfluxDBException); 204 | } 205 | 206 | SECTION("Write to unreachable host throws") 207 | { 208 | auto invalidDb = influxdb::InfluxDBFactory::Get("http://non_existing_host:1234?db=not_existing_db"); 209 | CHECK_THROWS_AS(invalidDb->write(Point{"test"}.addField("value", 10)), InfluxDBException); 210 | } 211 | 212 | SECTION("Write to nonexistent database throws") 213 | { 214 | auto invalidDb = configure("not_existing_db"); 215 | CHECK_THROWS_AS(invalidDb->write(Point{"test"}.addField("value", 10)), InfluxDBException); 216 | } 217 | 218 | SECTION("Cleanup") 219 | { 220 | db->execute("drop database st_db"); 221 | } 222 | } 223 | 224 | TEST_CASE("String Element Escaping", "[InfluxDBST]") 225 | { 226 | using namespace Catch::Matchers; 227 | 228 | const std::string dbName{"st_escaped_db"}; 229 | const std::string pointType{"escaped"}; 230 | auto db = configure(dbName); 231 | 232 | // String elements which need to be escaped in line protocol 233 | const std::string unescapedMeasurementName{"esc, measurement"}; 234 | const std::string unescapedTagKey{"esc,= tag"}; 235 | const std::string unescapedTagValue{"esc,= value"}; 236 | const std::string unescapedFieldKey{"esc,= field"}; 237 | const std::string unescapedFieldValue{R"(esc"\value)"}; 238 | 239 | SECTION("Create test database") 240 | { 241 | db->createDatabaseIfNotExists(); 242 | CHECK_THAT(db->execute("show databases"), ContainsSubstring(dbName)); 243 | } 244 | 245 | SECTION("Write point with escaped string elements") 246 | { 247 | db->createDatabaseIfNotExists(); 248 | CHECK(querySize(*db, unescapedMeasurementName, pointType) == 0); 249 | db->write(Point{unescapedMeasurementName}.addTag(unescapedTagKey, unescapedTagValue).addField(unescapedFieldKey, unescapedFieldValue).addTag("type", pointType)); 250 | CHECK(querySize(*db, unescapedMeasurementName, pointType) == 1); 251 | } 252 | 253 | SECTION("Queried point string elements should be unescaped") 254 | { 255 | const auto response{db->query(R"(select * from ")" + unescapedMeasurementName + R"(" where type=')" + pointType + "'")}; 256 | CHECK(response.size() == 1); 257 | 258 | const Point& point{response.at(0)}; 259 | 260 | // Measurement 261 | CHECK(point.getName() == unescapedMeasurementName); 262 | 263 | // Tags 264 | const Point::TagSet& tags{point.getTagSet()}; 265 | CHECK(tags.size() == 3); 266 | // Should contain the unescaped tag key and value 267 | CHECK(tags.end() != std::find(tags.begin(), tags.end(), Point::TagSet::value_type{unescapedTagKey, unescapedTagValue})); 268 | CHECK(tags.end() != std::find(tags.begin(), tags.end(), Point::TagSet::value_type{"type", "escaped"})); 269 | // Queried string values actually end up in the tags (see queryImpl) 270 | CHECK(tags.end() != std::find(tags.begin(), tags.end(), Point::TagSet::value_type{unescapedFieldKey, unescapedFieldValue})); 271 | 272 | // Fields 273 | const Point::FieldSet& fields{point.getFieldSet()}; 274 | // String fields are put in the tags (see above) 275 | CHECK(fields.size() == 0); 276 | } 277 | 278 | SECTION("Cleanup") 279 | { 280 | db->execute("drop database " + dbName); 281 | } 282 | } 283 | 284 | } 285 | -------------------------------------------------------------------------------- /test/system/SystemTest.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020-2025 offa 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #include "InfluxDB/InfluxDBFactory.h" 26 | #include "InfluxDB/InfluxDBException.h" 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | namespace influxdb::test 34 | { 35 | struct User 36 | { 37 | std::string name; 38 | std::string pass; 39 | }; 40 | 41 | 42 | inline std::optional getEnv(const std::string& name) 43 | { 44 | if (const auto value = std::getenv(name.c_str()); value != nullptr) 45 | { 46 | return value; 47 | } 48 | return {}; 49 | } 50 | 51 | inline User getUserFromEnv() 52 | { 53 | const auto user = getEnv("INFLUXDBCXX_SYSTEMTEST_USER"); 54 | const auto pass = getEnv("INFLUXDBCXX_SYSTEMTEST_PASSWORD"); 55 | 56 | if (!user || !pass) 57 | { 58 | SKIP("No authentication configured: 'INFLUXDBCXX_SYSTEMTEST_USER' and/or 'INFLUXDBCXX_SYSTEMTEST_PASSWORD' not set"); 59 | return {"", ""}; 60 | } 61 | return {*user, *pass}; 62 | } 63 | 64 | inline std::string getHostFromEnv() 65 | { 66 | return getEnv("INFLUXDBCXX_SYSTEMTEST_HOST").value_or("localhost"); 67 | } 68 | 69 | inline std::unique_ptr configure(const std::string& db, std::optional user = {}) 70 | { 71 | const std::string authString{user ? (user->name + ":" + user->pass + "@") : ""}; 72 | return InfluxDBFactory::Get("http://" + authString + getHostFromEnv() + ":8086?db=" + db); 73 | } 74 | } 75 | --------------------------------------------------------------------------------