├── .circleci └── config.yml ├── .github ├── ISSUE_TEMPLATE │ ├── BUG_REPORT.yml │ ├── FEATURE_REQUEST.yml │ └── SUPPORT.yml ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ ├── linter.yml │ └── semantic.yml ├── .gitignore ├── .markdownlint.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── checkstyle.xml ├── deploy-settings.xml ├── duke_logo.png ├── examples ├── .gitignore ├── README.md ├── docker-compose.yml ├── envoy.yaml ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── influxdb │ └── v3 │ ├── DownsamplingExample.java │ ├── IOxExample.java │ ├── ProxyExample.java │ └── RetryExample.java ├── license_header.txt ├── maven-version-rules.xml ├── pom.xml └── src ├── main └── java │ └── com │ └── influxdb │ └── v3 │ └── client │ ├── InfluxDBApiException.java │ ├── InfluxDBApiHttpException.java │ ├── InfluxDBClient.java │ ├── Point.java │ ├── PointValues.java │ ├── config │ └── ClientConfig.java │ ├── internal │ ├── Arguments.java │ ├── FlightSqlClient.java │ ├── GrpcCallOptions.java │ ├── Identity.java │ ├── InfluxDBClientImpl.java │ ├── NanosecondConverter.java │ ├── RestClient.java │ ├── TypeCasting.java │ └── VectorSchemaRootConverter.java │ ├── query │ ├── QueryOptions.java │ └── QueryType.java │ └── write │ ├── WriteOptions.java │ ├── WritePrecision.java │ └── WritePrecisionConverter.java ├── site └── site.xml └── test └── java └── com └── influxdb └── v3 └── client ├── AbstractMockServerTest.java ├── ITQueryWrite.java ├── InfluxDBClientTest.java ├── InfluxDBClientWriteTest.java ├── PointTest.java ├── PointValuesTest.java ├── config └── ClientConfigTest.java ├── integration └── E2ETest.java ├── internal ├── ArgumentsDurationTest.java ├── ArgumentsTest.java ├── FlightSqlClientTest.java ├── GrpcCallOptionsTest.java ├── NanosecondConverterTest.java ├── RestClientTest.java ├── TypeCastingTest.java ├── VectorSchemaRootConverterTest.java └── VectorSchemaRootUtils.java ├── query └── QueryOptionsTest.java ├── testdata ├── docker.com.pem └── influxdb-certificate.pem └── write ├── PointTest.java ├── WriteOptionsTest.java └── WritePrecisionConverterTest.java /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | jobs: 4 | tests-java: 5 | parameters: 6 | maven-image: 7 | type: string 8 | default: &default-maven-image "cimg/openjdk:11.0" 9 | arg-line: 10 | type: string 11 | default: "" 12 | docker: 13 | - image: << parameters.maven-image >> 14 | steps: 15 | - checkout 16 | - restore_cache: 17 | name: Restoring Maven Cache 18 | keys: 19 | - &cache-key maven-cache_v1-<< parameters.maven-image >>-{{ checksum "pom.xml" }} 20 | - maven-cache_v3-<< parameters.maven-image >>- 21 | - run: 22 | name: "Running tests" 23 | command: | 24 | mvn -B -U clean install -DargLine="@{argLine} << parameters.arg-line >>" 25 | - save_cache: 26 | name: Saving Maven Cache 27 | key: *cache-key 28 | paths: 29 | - ~/.m2 30 | - run: 31 | name: "Copying test results" 32 | when: always 33 | command: | 34 | mkdir test-results 35 | cp target/surefire-reports/*.xml test-results/ || true 36 | - store_test_results: 37 | path: test-results 38 | - run: 39 | name: "Copying artifacts" 40 | command: | 41 | mkdir artifacts 42 | cp -r target/*.jar artifacts/ 43 | - store_artifacts: 44 | path: artifacts 45 | - run: 46 | name: Collecting coverage reports 47 | command: | 48 | curl -Os https://uploader.codecov.io/latest/linux/codecov 49 | curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM 50 | curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM.sig 51 | curl -s https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --no-default-keyring --keyring trustedkeys.gpg --import 52 | gpgv codecov.SHA256SUM.sig codecov.SHA256SUM 53 | shasum -a 256 -c codecov.SHA256SUM 54 | chmod +x ./codecov 55 | ./codecov 56 | 57 | check-dependencies: 58 | parameters: 59 | maven-image: 60 | type: string 61 | default: *default-maven-image 62 | docker: 63 | - image: *default-maven-image 64 | steps: 65 | - checkout 66 | - restore_cache: 67 | name: Restoring Maven Cache 68 | keys: 69 | - maven-cache_v1-<< parameters.maven-image >>- 70 | - run: 71 | name: "Check dependency rules" 72 | command: mvn enforcer:enforce -Denforcer.rules=banDuplicatePomDependencyVersions,dependencyConvergence 73 | 74 | check-licenses: 75 | parameters: 76 | maven-image: 77 | type: string 78 | default: *default-maven-image 79 | docker: 80 | - image: << parameters.maven-image >> 81 | steps: 82 | - checkout 83 | - restore_cache: 84 | name: Restoring Maven Cache 85 | keys: 86 | - maven-cache_v1-<< parameters.maven-image >>- 87 | - run: 88 | name: "Check dependency licenses" 89 | command: mvn license:check -Dlicense.dependencies.enforce=true 90 | 91 | check-generate-site: 92 | parameters: 93 | maven-image: 94 | type: string 95 | default: *default-maven-image 96 | docker: 97 | - image: << parameters.maven-image >> 98 | steps: 99 | - checkout 100 | - restore_cache: 101 | name: Restoring Maven Cache 102 | keys: 103 | - maven-cache_v1-<< parameters.maven-image >>- 104 | - run: 105 | name: "Check generate site" 106 | command: mvn clean site site:stage -DskipTests 107 | 108 | deploy-snapshot: 109 | docker: 110 | - image: *default-maven-image 111 | steps: 112 | - run: 113 | name: Early return if this build is from a forked repository 114 | command: | 115 | if [[ $CIRCLE_PROJECT_USERNAME != "InfluxCommunity" ]]; then 116 | echo "Nothing to do for forked repositories, so marking this step successful" 117 | circleci step halt 118 | fi 119 | - checkout 120 | - run: 121 | name: Early return if this build is not a Snapshot version 122 | command: | 123 | sudo apt-get update 124 | sudo apt-get install libxml2-utils 125 | export PROJECT_VERSION=$(xmllint --xpath "//*[local-name()='project']/*[local-name()='version']/text()" pom.xml) 126 | echo "Project version: $PROJECT_VERSION" 127 | if [[ $PROJECT_VERSION != *SNAPSHOT ]]; then 128 | echo "Nothing to do for this build, so marking this step successful" 129 | circleci step halt 130 | fi 131 | - restore_cache: 132 | name: Restoring Maven Cache 133 | keys: 134 | - &cache-key-deploy maven-cache-deploy_v1-{{ checksum "pom.xml" }} 135 | - maven-cache-deploy_v1- 136 | - run: 137 | name: Deploying Snapshot 138 | command: | 139 | mvn -s ./deploy-settings.xml -DskipTests=true clean deploy 140 | - save_cache: 141 | name: Saving Maven Cache 142 | key: *cache-key-deploy 143 | paths: 144 | - ~/.m2 145 | 146 | workflows: 147 | version: 2 148 | build: 149 | jobs: 150 | - check-dependencies 151 | - check-licenses 152 | - check-generate-site 153 | - tests-java: 154 | name: jdk-11 155 | - tests-java: 156 | name: jdk-17 157 | maven-image: "cimg/openjdk:17.0" 158 | arg-line: "--add-opens=java.base/java.nio=ALL-UNNAMED" 159 | - tests-java: 160 | name: jdk-20 161 | maven-image: "cimg/openjdk:20.0" 162 | arg-line: "--add-opens=java.base/java.nio=ALL-UNNAMED" 163 | - tests-java: 164 | name: jdk-21 165 | maven-image: "cimg/openjdk:21.0" 166 | arg-line: "--add-opens=java.base/java.nio=ALL-UNNAMED" 167 | - deploy-snapshot: 168 | requires: 169 | - check-dependencies 170 | - check-licenses 171 | - check-generate-site 172 | - jdk-11 173 | - jdk-17 174 | - jdk-20 175 | - jdk-21 176 | filters: 177 | branches: 178 | only: main 179 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG_REPORT.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a bug report to help us improve 3 | labels: ["bug"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for taking time to fill out this bug report! We reserve this repository issues for bugs with reproducible problems. 9 | Please redirect any questions about the client usage to our [Community Slack](https://app.slack.com/huddle/TH8RGQX5Z/C02UDUPLQKA) or [Community Page](https://community.influxdata.com/) we have a lot of talented community members there who could help answer your question more quickly. 10 | 11 | * Please add a :+1: or comment on a similar existing bug report instead of opening a new one. 12 | * Please check whether the bug can be reproduced with the latest release. 13 | - type: textarea 14 | id: specifications 15 | attributes: 16 | label: Specifications 17 | description: Describe the steps to reproduce the bug. 18 | value: | 19 | * Client Version: 20 | * InfluxDB Version: 21 | * Platform: 22 | validations: 23 | required: true 24 | - type: textarea 25 | id: reproduce 26 | attributes: 27 | label: Code sample to reproduce problem 28 | description: Provide a code sample that reproduces the problem 29 | value: | 30 | ```java 31 | ``` 32 | validations: 33 | required: true 34 | - type: textarea 35 | id: expected-behavior 36 | attributes: 37 | label: Expected behavior 38 | description: Describe what you expected to happen when you performed the above steps. 39 | validations: 40 | required: true 41 | - type: textarea 42 | id: actual-behavior 43 | attributes: 44 | label: Actual behavior 45 | description: Describe what actually happened when you performed the above steps. 46 | validations: 47 | required: true 48 | - type: textarea 49 | id: additional-info 50 | attributes: 51 | label: Additional info 52 | description: Include gist of relevant config, logs, etc. 53 | validations: 54 | required: false -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Create a feature request to make client more awesome 3 | labels: ["feature request"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for taking time to share with us this feature request! Please describe why you would like this feature to be added to the client, how you plan to use it to make your life better. 9 | - type: textarea 10 | id: use-case 11 | attributes: 12 | label: Use Case 13 | description: Describe how you plan to use this feature. 14 | validations: 15 | required: true 16 | - type: textarea 17 | id: expected-behavior 18 | attributes: 19 | label: Expected behavior 20 | description: Describe what you expected to happen when you performed the above steps. 21 | validations: 22 | required: true 23 | - type: textarea 24 | id: actual-behavior 25 | attributes: 26 | label: Actual behavior 27 | description: Describe what actually happened when you performed the above steps. 28 | validations: 29 | required: true 30 | - type: textarea 31 | id: additional-info 32 | attributes: 33 | label: Additional info 34 | description: Include gist of relevant config, logs, etc. 35 | validations: 36 | required: false 37 | 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/SUPPORT.yml: -------------------------------------------------------------------------------- 1 | name: Support request 2 | description: Open a support request 3 | labels: ["support"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | WOAHH, hold up. This isn't the best place for support questions. 9 | You can get a faster response on slack or forums: 10 | 11 | Please redirect any QUESTIONS about Telegraf usage to 12 | - InfluxData Slack Channel: https://app.slack.com/huddle/TH8RGQX5Z/C02UDUPLQKA 13 | - InfluxData Community Site: https://community.influxdata.com 14 | 15 | - type: textarea 16 | attributes: 17 | label: "Please direct all support questions to slack or the forums. Thank you." 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Closes # 2 | 3 | ## Proposed Changes 4 | 5 | _Briefly describe your proposed changes:_ 6 | 7 | ## Checklist 8 | 9 | 10 | 11 | - [ ] CHANGELOG.md updated 12 | - [ ] Rebased/mergeable 13 | - [ ] A test has been added if appropriate 14 | - [ ] Tests pass 15 | - [ ] Commit messages are [conventional](https://www.conventionalcommits.org/en/v1.0.0/) 16 | - [ ] Sign [CLA](https://www.influxdata.com/legal/cla/) (if not already signed) 17 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "maven" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | CodeQL-Build: 11 | runs-on: ubuntu-latest 12 | 13 | permissions: 14 | security-events: write 15 | actions: read 16 | contents: read 17 | 18 | steps: 19 | - name: Checkout repository 20 | uses: actions/checkout@v3 21 | 22 | - name: Initialize CodeQL 23 | uses: github/codeql-action/init@v2 24 | with: 25 | languages: java 26 | 27 | - name: Autobuild 28 | uses: github/codeql-action/autobuild@v2 29 | 30 | - name: Perform CodeQL Analysis 31 | uses: github/codeql-action/analyze@v2 32 | -------------------------------------------------------------------------------- /.github/workflows/linter.yml: -------------------------------------------------------------------------------- 1 | name: "Lint Code Base" 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | permissions: 12 | contents: read 13 | packages: read 14 | statuses: write 15 | 16 | name: Lint Code Base 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Checkout Code 21 | uses: actions/checkout@v3 22 | with: 23 | fetch-depth: 0 24 | 25 | - name: Lint Code Base 26 | uses: github/super-linter@v4.9.2 27 | env: 28 | VALIDATE_ALL_CODEBASE: false 29 | DEFAULT_BRANCH: main 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | LINTER_RULES_PATH: '.' 32 | MARKDOWN_CONFIG_FILE: .markdownlint.yml 33 | VALIDATE_MARKDOWN: true 34 | VALIDATE_BASH: true 35 | VALIDATE_JAVA: true 36 | JAVA_FILE_NAME: 'checkstyle.xml' 37 | VALIDATE_GITHUB_ACTIONS: true 38 | -------------------------------------------------------------------------------- /.github/workflows/semantic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Semantic PR and Commit Messages" 3 | 4 | on: 5 | pull_request: 6 | types: [opened, reopened, synchronize, edited] 7 | branches: 8 | - main 9 | 10 | jobs: 11 | semantic: 12 | uses: influxdata/validate-semantic-github-messages/.github/workflows/semantic.yml@main 13 | with: 14 | CHECK_PR_TITLE_OR_ONE_COMMIT: true 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target/ 3 | .java-version 4 | -------------------------------------------------------------------------------- /.markdownlint.yml: -------------------------------------------------------------------------------- 1 | { 2 | "MD013": false, 3 | "MD024": { 4 | "siblings_only": true 5 | }, 6 | "MD033": { 7 | "allowed_elements": [ "a", "img", "p" ] 8 | }, 9 | "MD041": false, 10 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.2.0 [unreleased] 2 | 3 | ### Features 4 | 5 | 1. [#209](https://github.com/InfluxCommunity/influxdb3-java/pull/209) Add query function returning row as map. 6 | 1. [#238](https://github.com/InfluxCommunity/influxdb3-java/pull/238): Support fast writes without waiting for WAL 7 | persistence: 8 | - New write option (`WriteOptions.noSync`) added: `true` value means faster write but without the confirmation that 9 | the data was persisted. Default value: `false`. 10 | - **Supported by self-managed InfluxDB 3 Core and Enterprise servers only!** 11 | - Also configurable via connection string query parameter (`writeNoSync`). 12 | - Also configurable via environment variable (`INFLUX_WRITE_NO_SYNC`). 13 | - Long precision string values added from v3 HTTP API: `"nanosecond"`, `"microsecond"`, `"millisecond"`, 14 | `"second"` ( 15 | in addition to the existing `"ns"`, `"us"`, `"ms"`, `"s"`). 16 | 17 | ### Bug Fixes 18 | 19 | 1. [#239](https://github.com/InfluxCommunity/influxdb3-java/pull/239): Use write options from `ClientConfig` in 20 | `InfluxDBClientImpl` write methods: 21 | 22 | ```java 23 | public void writeRecord(@Nullable final String record); 24 | public void writeRecords(@Nonnull final List records); 25 | public void writePoint(@Nullable final Point point); 26 | public void writePoints(@Nonnull final List points); 27 | ``` 28 | 29 | ## 1.1.0 [2025-05-22] 30 | 31 | ### Features 32 | 33 | 1. [#229](https://github.com/InfluxCommunity/influxdb3-java/pull/229): Support proxy and custom ssl root certificates 34 | 2. [#232](https://github.com/InfluxCommunity/influxdb3-java/pull/232): Allow set rpc max message size through maxInboundMessageSize in ClientConfig 35 | 3. [#233](https://github.com/InfluxCommunity/influxdb3-java/pull/233): More detailed documentation about timestamp handling for query and write functions 36 | 4. [#236](https://github.com/InfluxCommunity/influxdb3-java/pull/236): Supports Java 21. 37 | 38 | ## 1.0.0 [2024-12-11] 39 | 40 | ### Features 41 | 42 | 1. [#200](https://github.com/InfluxCommunity/influxdb3-java/pull/200): Respect iox::column_type::field metadata when 43 | mapping query results into values. 44 | - iox::column_type::field::integer: => Long 45 | - iox::column_type::field::uinteger: => Long 46 | - iox::column_type::field::float: => Double 47 | - iox::column_type::field::string: => String 48 | - iox::column_type::field::boolean: => Boolean 49 | 50 | ### Dependencies 51 | 52 | 1. [#202](https://github.com/InfluxCommunity/influxdb3-java/pull/202): Migrate from `flight-grpc` to `flight-core` package. 53 | 54 | ## 0.9.0 [2024-08-12] 55 | 56 | ### Features 57 | 58 | 1. [#158](https://github.com/InfluxCommunity/influxdb3-java/pull/158): Add InfluxDB Edge (OSS) authentication support. 59 | 1. [#163](https://github.com/InfluxCommunity/influxdb3-java/pull/163): Introduces `InfluxDBApiHttpException` to facilitate write retries and error recovery. 60 | 61 | ### Bug Fixes 62 | 63 | 1. [#148](https://github.com/InfluxCommunity/influxdb3-java/pull/148): InfluxDB Edge (OSS) error handling 64 | 1. [#153](https://github.com/InfluxCommunity/influxdb3-java/pull/153): Parsing timestamp columns 65 | 66 | ## 0.8.0 [2024-06-24] 67 | 68 | ### Features 69 | 70 | 1. [#144](https://github.com/InfluxCommunity/influxdb3-java/pull/133): user-agent header is updated for both REST and gRPC calls. 71 | 72 | ## 0.7.0 [2024-03-11] 73 | 74 | ### Features 75 | 76 | 1. [#107](https://github.com/InfluxCommunity/influxdb3-java/pull/107): Custom headers are also supported for the query (gRPC request) 77 | 78 | ```java 79 | ClientConfig config = new ClientConfig.Builder() 80 | .host("https://us-east-1-1.aws.cloud2.influxdata.com") 81 | .token("my-token".toCharArray()) 82 | .database("my-database") 83 | .headers(Map.of("X-Tracing-Id", "123")) 84 | .build(); 85 | 86 | try (InfluxDBClient client = InfluxDBClient.getInstance(config)) { 87 | // 88 | // your code here 89 | // 90 | } catch (Exception e) { 91 | throw new RuntimeException(e); 92 | } 93 | ``` 94 | 95 | 1. [#108](https://github.com/InfluxCommunity/influxdb3-java/pull/108): Custom headers can be specified per request (query/write): 96 | 97 | ```java 98 | ClientConfig config = new ClientConfig.Builder() 99 | .host("https://us-east-1-1.aws.cloud2.influxdata.com") 100 | .token("my-token".toCharArray()) 101 | .database("my-database") 102 | .build(); 103 | 104 | try (InfluxDBClient client = InfluxDBClient.getInstance(config)) { 105 | // 106 | // Write with custom headers 107 | // 108 | WriteOptions writeOptions = new WriteOptions( 109 | Map.of("X-Tracing-Id", "852") 110 | ); 111 | client.writeRecord("mem,tag=one value=1.0", writeOptions); 112 | 113 | // 114 | // Query with custom headers 115 | // 116 | QueryOptions queryOptions = new QueryOptions( 117 | Map.of("X-Tracing-Id", "852") 118 | ); 119 | Stream rows = client.query("select * from cpu", queryOptions); 120 | 121 | } catch (Exception e) { 122 | throw new RuntimeException(e); 123 | } 124 | ``` 125 | 126 | ## 0.6.0 [2024-03-01] 127 | 128 | ### Features 129 | 130 | 1. [#94](https://github.com/InfluxCommunity/influxdb3-java/pull/94): Add support for named query parameters 131 | 132 | ## 0.5.1 [2024-02-01] 133 | 134 | Resync artifacts with Maven Central. 135 | 136 | ## 0.5.0 [2024-01-30] 137 | 138 | ### Features 139 | 140 | 1. [#78](https://github.com/InfluxCommunity/influxdb3-java/pull/78): Default Tags can be used when writing points. 141 | 142 | ### Bug Fixes 143 | 144 | 1. [#77](https://github.com/InfluxCommunity/influxdb3-java/pull/77): Serialize InfluxDB response to `PointValues` 145 | 146 | ## 0.4.0 [2023-11-08] 147 | 148 | ### Features 149 | 150 | 1. [#41](https://github.com/InfluxCommunity/influxdb3-java/pull/41): Add structured query support 151 | 152 | ## 0.3.1 [2023-10-17] 153 | 154 | ### Bug Fixes 155 | 156 | 1. [#55](https://github.com/InfluxCommunity/influxdb3-java/pull/55): Iteration over more Arrow streams 157 | 158 | ## 0.3.0 [2023-10-02] 159 | 160 | ### Features 161 | 162 | 1. [#40](https://github.com/InfluxCommunity/influxdb3-java/pull/40): Add client creation from connection string, 163 | environment variables or system properties. 164 | 165 | ## 0.2.0 [2023-08-11] 166 | 167 | ### Features 168 | 169 | 1. [#27](https://github.com/InfluxCommunity/influxdb3-java/pull/27): Add GZIP support 170 | 1. [#30](https://github.com/InfluxCommunity/influxdb3-java/pull/30): Add HTTP proxy and custom headers support 171 | 172 | ### Breaking Changes 173 | 174 | 1. [#31](https://github.com/InfluxCommunity/influxdb3-java/pull/31): Renamed config types and some options 175 | 176 | ## 0.1.0 [2023-06-08] 177 | 178 | - initial release of new client version 179 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 InfluxData Inc. 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Duke 3 |

4 |

5 | 6 | Maven Central Badge 7 | 8 | 9 | Maven Site 10 | 11 | 12 | CodeQL analysis 13 | 14 | 15 | Lint Code Base 16 | 17 | 18 | CircleCI 19 | 20 | 21 | Code Cov 22 | 23 | 24 | Community Slack 25 | 26 |

27 | 28 | # InfluxDB 3 Java Client 29 | 30 | The Java client that provides an easy and convenient way to interact with InfluxDB 3. 31 | This package supports both writing data to InfluxDB and querying data using the FlightSQL client, 32 | which allows you to execute SQL queries against InfluxDB IOx. 33 | 34 | We offer this [Getting Started: InfluxDB 3.0 Java Client Library](https://www.youtube.com/watch?v=EFnG7rUDvR4) video for learning more about the library. 35 | 36 | > :warning: This client requires Java 11 and is compatible up to and including Java 21. 37 | 38 | ## Installation 39 | 40 | > :warning: Some JDK internals must be exposed by adding `--add-opens=java.base/java.nio=ALL-UNNAMED` to your JVM arguments. 41 | 42 | Add the latest version of the client to your project: 43 | 44 | ### Maven dependency 45 | 46 | ```xml 47 | 48 | com.influxdb 49 | influxdb3-java 50 | 1.1.0 51 | 52 | ``` 53 | 54 | ### Or when using Gradle 55 | 56 | ```groovy 57 | dependencies { 58 | implementation "com.influxdb:influxdb3-java:1.1.0" 59 | } 60 | ``` 61 | 62 | ## Usage 63 | 64 | To start with the client, import the `com.influxdb.v3.client` package and create a `InfluxDBClient` by: 65 | 66 | ```java 67 | package com.influxdb.v3; 68 | 69 | import java.time.Instant; 70 | import java.util.stream.Stream; 71 | 72 | import com.influxdb.v3.client.InfluxDBClient; 73 | import com.influxdb.v3.client.query.QueryOptions; 74 | import com.influxdb.v3.client.Point; 75 | 76 | public class IOxExample { 77 | public static void main(String[] args) throws Exception { 78 | String host = "https://us-east-1-1.aws.cloud2.influxdata.com"; 79 | char[] token = "my-token".toCharArray(); 80 | String database = "database"; 81 | 82 | try (InfluxDBClient client = InfluxDBClient.getInstance(host, token, database)) { 83 | // ... 84 | } 85 | } 86 | } 87 | ``` 88 | 89 | to insert data, you can use code like this: 90 | 91 | ```java 92 | // 93 | // Write by Point 94 | // 95 | Point point = Point.measurement("temperature") 96 | .setTag("location", "west") 97 | .setField("value", 55.15) 98 | .setTimestamp(Instant.now().minusSeconds(-10)); 99 | client.writePoint(point); 100 | 101 | // 102 | // Write by LineProtocol 103 | // 104 | String record = "temperature,location=north value=60.0"; 105 | client.writeRecord(record); 106 | ``` 107 | 108 | to query your data, you can use code like this: 109 | 110 | ```java 111 | // 112 | // Query by SQL 113 | // 114 | System.out.printf("--------------------------------------------------------%n"); 115 | System.out.printf("| %-8s | %-8s | %-30s |%n", "location", "value", "time"); 116 | System.out.printf("--------------------------------------------------------%n"); 117 | 118 | String sql = "select time,location,value from temperature order by time desc limit 10"; 119 | try (Stream stream = client.query(sql)) { 120 | stream.forEach(row -> System.out.printf("| %-8s | %-8s | %-30s |%n", row[1], row[2], row[0])); 121 | } 122 | 123 | System.out.printf("--------------------------------------------------------%n%n"); 124 | 125 | // 126 | // Query by parametrized SQL 127 | // 128 | System.out.printf("--------------------Parametrized SQL--------------------%n"); 129 | System.out.printf("| %-8s | %-8s | %-30s |%n", "location", "value", "time"); 130 | System.out.printf("--------------------------------------------------------%n"); 131 | 132 | String sqlParams = "select time,location,value from temperature where location=$location order by time desc limit 10"; 133 | try (Stream stream = client.query(sqlParams, Map.of("location", "north"))) { 134 | stream.forEach(row -> System.out.printf("| %-8s | %-8s | %-30s |%n", row[1], row[2], row[0])); 135 | } 136 | 137 | System.out.printf("--------------------------------------------------------%n%n"); 138 | 139 | // 140 | // Query by InfluxQL 141 | // 142 | System.out.printf("-----------------------------------------%n"); 143 | System.out.printf("| %-16s | %-18s |%n", "time", "mean"); 144 | System.out.printf("-----------------------------------------%n"); 145 | 146 | String influxQL = "select MEAN(value) from temperature group by time(1d) fill(none) order by time desc limit 10"; 147 | try (Stream stream = client.query(influxQL, QueryOptions.INFLUX_QL)) { 148 | stream.forEach(row -> System.out.printf("| %-16s | %-18s |%n", row[1], row[2])); 149 | } 150 | 151 | System.out.printf("-----------------------------------------%n"); 152 | ``` 153 | 154 | or use `PointValues` structure with `client.queryPoints`: 155 | 156 | ```java 157 | System.out.printf("--------------------------------------------------------%n"); 158 | System.out.printf("| %-8s | %-8s | %-30s |%n", "location", "value", "time"); 159 | System.out.printf("--------------------------------------------------------%n"); 160 | 161 | // 162 | // Query by SQL into Points 163 | // 164 | String sql1 = "select time,location,value from temperature order by time desc limit 10"; 165 | try (Stream stream = client.queryPoints(sql1, QueryOptions.DEFAULTS)) { 166 | stream.forEach( 167 | (PointValues p) -> { 168 | var time = p.getTimestamp(); 169 | var location = p.getTag("location"); 170 | var value = p.getField("value", Double.class); 171 | 172 | System.out.printf("| %-8s | %-8s | %-30s |%n", location, value, time); 173 | }); 174 | } 175 | 176 | System.out.printf("--------------------------------------------------------%n%n"); 177 | ``` 178 | 179 | ## Feedback 180 | 181 | If you need help, please use our [Community Slack](https://app.slack.com/huddle/TH8RGQX5Z/C02UDUPLQKA) 182 | or [Community Page](https://community.influxdata.com/). 183 | 184 | New features and bugs can be reported on GitHub: 185 | 186 | ## Contribution 187 | 188 | If you would like to contribute code you can do through GitHub by forking the repository and sending a pull request into 189 | the `main` branch. 190 | 191 | ## License 192 | 193 | The InfluxDB 3 Java Client is released under the [MIT License](https://opensource.org/licenses/MIT). 194 | -------------------------------------------------------------------------------- /checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /deploy-settings.xml: -------------------------------------------------------------------------------- 1 | 24 | 26 | 27 | 28 | ossrh 29 | ${env.SONATYPE_USERNAME} 30 | ${env.SONATYPE_PASSWORD} 31 | 32 | 33 | 34 | 35 | 36 | ossrh 37 | 38 | true 39 | 40 | 41 | ${env.GPG_EXECUTABLE} 42 | ${env.GPG_PASSPHRASE} 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /duke_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InfluxCommunity/influxdb3-java/d1560cb6d47b0311e88b6fa84708f24e1ed8322f/duke_logo.png -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | > :warning: The examples depends on the "influxdb3-java" module and this module should be built first by running "mvn install" in the root directory. 4 | > :warning: Some JDK internals must be exposed by adding `--add-opens=java.base/java.nio=ALL-UNNAMED` to your JVM arguments. 5 | 6 | ## Basic 7 | 8 | - [IOxExample](src/main/java/com/influxdb/v3/IOxExample.java) - How to use write and query data from InfluxDB IOx 9 | 10 | ## RetryExample 11 | 12 | - [RetryExample](src/main/java/com/influxdb/v3/RetryExample.java) - How to catch an `InfluxDBApiHttpException` to then retry making write requests. 13 | 14 | ### Command line run 15 | 16 | - Set environment variables. 17 | 18 | ```bash 19 | 20 | export INFLUX_HOST= 21 | export INFLUX_TOKEN= 22 | export INFLUX_DATABASE= 23 | 24 | ``` 25 | 26 | - Run with maven 27 | 28 | ```bash 29 | mvn compile exec:java -Dexec.main="com.influxdb.v3.RetryExample" 30 | ``` 31 | 32 | - Repeat previous step to force an HTTP 429 response and rewrite attempt. 33 | -------------------------------------------------------------------------------- /examples/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # 2 | # The MIT License 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | # 22 | 23 | version: "3" 24 | 25 | services: 26 | envoy: 27 | image: envoyproxy/envoy:v1.26-latest 28 | volumes: 29 | - ./envoy.yaml:/etc/envoy/envoy.yaml 30 | ports: 31 | - "10000:10000" 32 | environment: 33 | - ENVOY_UID=0 34 | 35 | -------------------------------------------------------------------------------- /examples/envoy.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # The MIT License 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | # 22 | 23 | static_resources: 24 | listeners: 25 | - name: listener_0 26 | address: 27 | socket_address: { address: 0.0.0.0, port_value: 10000 } 28 | filter_chains: 29 | - filter_chain_match: 30 | filters: 31 | - name: envoy.filters.network.http_connection_manager 32 | typed_config: 33 | "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager 34 | stat_prefix: ingress_http 35 | access_log: 36 | - name: envoy.access_loggers.stdout 37 | typed_config: 38 | "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog 39 | http2_protocol_options: 40 | allow_connect: true 41 | upgrade_configs: 42 | - upgrade_type: CONNECT 43 | http_filters: 44 | - name: envoy.filters.http.router 45 | typed_config: 46 | "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router 47 | 48 | route_config: 49 | name: local_route 50 | virtual_hosts: 51 | - name: local_service 52 | domains: [ "*" ] 53 | routes: 54 | - match: 55 | connect_matcher: { } 56 | route: 57 | cluster: influxdb_cluster 58 | upgrade_configs: 59 | upgrade_type: CONNECT 60 | connect_config: { } 61 | - match: 62 | prefix: "/" 63 | route: 64 | cluster: influxdb_cluster 65 | prefix_rewrite: "/" 66 | auto_host_rewrite: true 67 | timeout: 10s 68 | cors: 69 | allow_origin_string_match: 70 | - prefix: "*" 71 | allow_methods: GET, PUT, DELETE, POST, OPTIONS 72 | clusters: 73 | - name: influxdb_cluster 74 | connect_timeout: 10s 75 | type: STRICT_DNS 76 | load_assignment: 77 | cluster_name: influxdb_cluster 78 | endpoints: 79 | - lb_endpoints: 80 | - endpoint: 81 | address: 82 | socket_address: 83 | address: "us-east-1-1.aws.cloud2.influxdata.com" 84 | port_value: 443 -------------------------------------------------------------------------------- /examples/pom.xml: -------------------------------------------------------------------------------- 1 | 24 | 26 | 4.0.0 27 | 28 | com.influxdb.v3 29 | examples 30 | 1.0-SNAPSHOT 31 | jar 32 | 33 | 34 | UTF-8 35 | com.influxdb.v3.IOxExample 36 | 37 | 38 | 39 | 40 | com.influxdb 41 | influxdb3-java 42 | 1.2.0-SNAPSHOT 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.codehaus.mojo 50 | exec-maven-plugin 51 | 1.2.1 52 | 53 | 54 | 55 | java 56 | 57 | 58 | 59 | 60 | ${exec.main} 61 | 62 | 63 | 64 | org.apache.maven.plugins 65 | maven-compiler-plugin 66 | 3.10.1 67 | 68 | 11 69 | 70 | 71 | 72 | 73 | org.apache.maven.plugins 74 | maven-checkstyle-plugin 75 | 3.2.1 76 | 77 | true 78 | ../checkstyle.xml 79 | true 80 | false 81 | 82 | src/main/java 83 | 84 | 85 | 86 | 87 | verify 88 | 89 | checkstyle 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /examples/src/main/java/com/influxdb/v3/DownsamplingExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3; 23 | 24 | import java.time.Instant; 25 | import java.time.LocalDateTime; 26 | import java.time.ZoneOffset; 27 | import java.time.temporal.ChronoUnit; 28 | import java.util.stream.Stream; 29 | 30 | import com.influxdb.v3.client.InfluxDBClient; 31 | import com.influxdb.v3.client.Point; 32 | import com.influxdb.v3.client.PointValues; 33 | 34 | /** 35 | * The example depends on the "influxdb3-java" module and this module should be built first 36 | * by running "mvn install" in the root directory. 37 | */ 38 | public final class DownsamplingExample { 39 | private DownsamplingExample() { 40 | } 41 | 42 | public static void main(final String[] args) throws Exception { 43 | String host = "https://us-east-1-1.aws.cloud2.influxdata.com"; 44 | String token = "my-token"; 45 | String database = "my-database"; 46 | 47 | try (InfluxDBClient client = InfluxDBClient.getInstance(host, token.toCharArray(), database)) { 48 | // 49 | // Write data 50 | // 51 | Point point1 = Point.measurement("stat") 52 | .setTag("unit", "temperature") 53 | .setField("avg", 24.5) 54 | .setField("max", 45.0) 55 | .setTimestamp(Instant.now().minus(20, ChronoUnit.MINUTES)); 56 | client.writePoint(point1); 57 | 58 | Point point2 = Point.measurement("stat") 59 | .setTag("unit", "temperature") 60 | .setField("avg", 28.0) 61 | .setField("max", 40.3) 62 | .setTimestamp(Instant.now().minus(10, ChronoUnit.MINUTES)); 63 | client.writePoint(point2); 64 | 65 | Point point3 = Point.measurement("stat") 66 | .setTag("unit", "temperature") 67 | .setField("avg", 20.5) 68 | .setField("max", 49.0) 69 | .setTimestamp(Instant.now()); 70 | client.writePoint(point3); 71 | 72 | // 73 | // Query downsampled data 74 | // 75 | String sql = "SELECT\n" 76 | + " date_bin('5 minutes', \"time\") as window_start,\n" 77 | + " AVG(\"avg\") as avg,\n" 78 | + " MAX(\"max\") as max\n" 79 | + " FROM \"stat\"\n" 80 | + " WHERE\n" 81 | + " \"time\" >= now() - interval '1 hour'\n" 82 | + " GROUP BY window_start\n" 83 | + " ORDER BY window_start ASC;\n"; 84 | 85 | 86 | // 87 | // Execute downsampling query into pointValues 88 | // 89 | try (Stream stream = client.queryPoints(sql)) { 90 | stream.forEach( 91 | (PointValues row) -> { 92 | var timestamp = row.getField("window_start", LocalDateTime.class); 93 | 94 | if (timestamp == null) { 95 | return; 96 | } 97 | 98 | System.out.printf("%s: avg is %s, max is %s%n", 99 | timestamp, row.getFloatField("avg"), row.getFloatField("max")); 100 | 101 | // 102 | // write back downsampled date to 'stat_downsampled' measurement 103 | // 104 | var downsampledPoint = row 105 | .asPoint("stat_downsampled") 106 | .removeField("window_start") 107 | .setTimestamp(timestamp.toInstant(ZoneOffset.UTC)); 108 | 109 | client.writePoint(downsampledPoint); 110 | }); 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /examples/src/main/java/com/influxdb/v3/IOxExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3; 23 | 24 | import java.time.Instant; 25 | import java.util.Map; 26 | import java.util.stream.Stream; 27 | 28 | import com.influxdb.v3.client.InfluxDBClient; 29 | import com.influxdb.v3.client.Point; 30 | import com.influxdb.v3.client.PointValues; 31 | import com.influxdb.v3.client.query.QueryOptions; 32 | 33 | /** 34 | * The example depends on the "influxdb3-java" module and this module should be built first 35 | * by running "mvn install" in the root directory. 36 | */ 37 | public final class IOxExample { 38 | private IOxExample() { 39 | } 40 | 41 | public static void main(final String[] args) throws Exception { 42 | String host = "https://us-east-1-1.aws.cloud2.influxdata.com"; 43 | String token = "my-token"; 44 | String database = "my-database"; 45 | 46 | try (InfluxDBClient client = InfluxDBClient.getInstance(host, token.toCharArray(), database)) { 47 | 48 | // 49 | // Write by Point 50 | // 51 | Point point = Point.measurement("temperature") 52 | .setTag("location", "west") 53 | .setField("value", 55.15) 54 | .setTimestamp(Instant.now().minusSeconds(-10)); 55 | client.writePoint(point); 56 | 57 | // 58 | // Write by LineProtocol 59 | // 60 | String record = "temperature,location=north value=60.0"; 61 | client.writeRecord(record); 62 | 63 | // 64 | // Query by SQL 65 | // 66 | System.out.printf("--------------------------------------------------------%n"); 67 | System.out.printf("| %-8s | %-8s | %-30s |%n", "location", "value", "time"); 68 | System.out.printf("--------------------------------------------------------%n"); 69 | 70 | String sql = "select time,location,value from temperature order by time desc limit 10"; 71 | try (Stream stream = client.query(sql)) { 72 | stream.forEach(row -> System.out.printf("| %-8s | %-8s | %-30s |%n", row[1], row[2], row[0])); 73 | } 74 | 75 | System.out.printf("--------------------------------------------------------%n%n"); 76 | 77 | // 78 | // Query by parametrized SQL 79 | // 80 | System.out.printf("--------------------Parametrized SQL--------------------%n"); 81 | System.out.printf("| %-8s | %-8s | %-30s |%n", "location", "value", "time"); 82 | System.out.printf("--------------------------------------------------------%n"); 83 | 84 | String sqlWithParameters = 85 | "select time,location,value from temperature where location=$location order by time desc limit 10"; 86 | try (Stream stream = client.query(sqlWithParameters, Map.of("location", "north"))) { 87 | stream.forEach(row -> System.out.printf("| %-8s | %-8s | %-30s |%n", row[1], row[2], row[0])); 88 | } 89 | 90 | System.out.printf("--------------------------------------------------------%n%n"); 91 | 92 | // 93 | // Query by InfluxQL 94 | // 95 | System.out.printf("-----------------------------------------%n"); 96 | System.out.printf("| %-16s | %-18s |%n", "time", "mean"); 97 | System.out.printf("-----------------------------------------%n"); 98 | 99 | String influxQL = 100 | "select MEAN(value) from temperature group by time(1d) fill(none) order by time desc limit 10"; 101 | try (Stream stream = client.query(influxQL, QueryOptions.INFLUX_QL)) { 102 | stream.forEach(row -> System.out.printf("| %-16s | %-18s |%n", row[1], row[2])); 103 | } 104 | 105 | System.out.printf("-----------------------------------------%n%n"); 106 | 107 | 108 | System.out.printf("--------------------------------------------------------%n"); 109 | System.out.printf("| %-8s | %-8s | %-30s |%n", "location", "value", "time"); 110 | System.out.printf("--------------------------------------------------------%n"); 111 | 112 | // 113 | // Query by SQL into Points 114 | // 115 | try (Stream stream = client.queryPoints(sql)) { 116 | stream.forEach( 117 | (PointValues p) -> { 118 | var time = p.getTimestamp(); 119 | var location = p.getTag("location"); 120 | var value = p.getField("value", Double.class); 121 | 122 | System.out.printf("| %-8s | %-8s | %-30s |%n", location, value, time); 123 | }); 124 | } 125 | 126 | System.out.printf("--------------------------------------------------------%n%n"); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /examples/src/main/java/com/influxdb/v3/ProxyExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3; 23 | 24 | import java.util.UUID; 25 | import java.util.stream.Stream; 26 | 27 | import com.influxdb.v3.client.InfluxDBClient; 28 | import com.influxdb.v3.client.Point; 29 | import com.influxdb.v3.client.PointValues; 30 | import com.influxdb.v3.client.config.ClientConfig; 31 | 32 | public final class ProxyExample { 33 | 34 | private ProxyExample() { 35 | } 36 | 37 | public static void main(final String[] args) throws Exception { 38 | // Run docker-compose.yml file to start Envoy proxy 39 | 40 | String proxyUrl = "http://localhost:10000"; 41 | String sslRootsFilePath = "src/test/java/com/influxdb/v3/client/testdata/influxdb-certificate.pem"; 42 | ClientConfig clientConfig = new ClientConfig.Builder() 43 | .host(System.getenv("INFLUXDB_URL")) 44 | .token(System.getenv("INFLUXDB_TOKEN").toCharArray()) 45 | .database(System.getenv("INFLUXDB_DATABASE")) 46 | .proxyUrl(proxyUrl) 47 | .sslRootsFilePath(sslRootsFilePath) 48 | .build(); 49 | 50 | InfluxDBClient influxDBClient = InfluxDBClient.getInstance(clientConfig); 51 | String testId = UUID.randomUUID().toString(); 52 | Point point = Point.measurement("My_Home") 53 | .setTag("room", "Kitchen") 54 | .setField("temp", 12.7) 55 | .setField("hum", 37) 56 | .setField("testId", testId); 57 | influxDBClient.writePoint(point); 58 | 59 | String query = String.format("SELECT * FROM \"My_Home\" WHERE \"testId\" = '%s'", testId); 60 | try (Stream stream = influxDBClient.queryPoints(query)) { 61 | stream.findFirst().ifPresent(values -> { 62 | assert values.getTimestamp() != null; 63 | System.out.printf("room[%s]: %s, temp: %3.2f, hum: %d", 64 | new java.util.Date(values.getTimestamp().longValue() / 1000000), 65 | values.getTag("room"), 66 | (Double) values.getField("temp"), 67 | (Long) values.getField("hum")); 68 | }); 69 | } 70 | } 71 | } 72 | 73 | -------------------------------------------------------------------------------- /examples/src/main/java/com/influxdb/v3/RetryExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3; 23 | 24 | import java.time.Instant; 25 | import java.time.LocalDateTime; 26 | import java.time.ZoneOffset; 27 | import java.time.format.DateTimeParseException; 28 | import java.time.temporal.ChronoUnit; 29 | import java.util.ArrayList; 30 | import java.util.List; 31 | import java.util.UUID; 32 | import java.util.concurrent.TimeUnit; 33 | import java.util.logging.Logger; 34 | 35 | import com.influxdb.v3.client.InfluxDBApiHttpException; 36 | import com.influxdb.v3.client.InfluxDBClient; 37 | import com.influxdb.v3.client.Point; 38 | 39 | 40 | /** 41 | * This examples shows how to catch certain HttpExceptions and leverage their status code and header 42 | * values to attempt to rewrite data refused by the server. It attempts to exceed the maximum allowable writes 43 | * for an unpaid Influx cloud account. 44 | *

45 | * A First run with 100,000 records should succeed. but a follow-up run within a couple of minutes 46 | * should result in a refusal with the HTTP status code 429 "Too Many Requests" 47 | *

48 | * See the examples README.md for more information. 49 | */ 50 | public final class RetryExample { 51 | 52 | private static final java.util.logging.Logger LOG = Logger.getLogger(RetryExample.class.getName()); 53 | 54 | private RetryExample() { } 55 | 56 | static int retries = 0; 57 | 58 | public static String resolveProperty(final String property, final String fallback) { 59 | return System.getProperty(property, System.getenv(property)) == null 60 | ? fallback : System.getProperty(property, System.getenv(property)); 61 | } 62 | 63 | public static void main(final String[] args) throws Exception { 64 | 65 | String host = resolveProperty("INFLUX_HOST", "https://us-east-1-1.aws.cloud2.influxdata.com"); 66 | String token = resolveProperty("INFLUX_TOKEN", "my-token"); 67 | String database = resolveProperty("INFLUX_DATABASE", "my-database"); 68 | 69 | Instant now = Instant.now(); 70 | int dataSetSize = 100000; 71 | 72 | LOG.info(String.format("Preparing to write %d records.\n", dataSetSize)); 73 | 74 | List packs = new ArrayList<>(); 75 | List points = new ArrayList<>(); 76 | for (int i = 0; i < dataSetSize; i++) { 77 | packs.add(BatteryPack.createRandom()); 78 | points.add(packs.get(i).toPoint(now.minus((dataSetSize - i) * 10, ChronoUnit.MILLIS))); 79 | } 80 | 81 | retries = 1; 82 | try (InfluxDBClient client = InfluxDBClient.getInstance(host, token.toCharArray(), database)) { 83 | writeWithRetry(client, points, retries); 84 | } 85 | } 86 | 87 | /** 88 | * Resolves the retry value in milliseconds. 89 | * 90 | * @param retryVal - retry value from the HTTP header "retry-after" 91 | * @return - wait interval in milliseconds 92 | */ 93 | private static long resolveRetry(final String retryVal) { 94 | try { 95 | return (Long.parseLong(retryVal) * 1000); 96 | } catch (NumberFormatException nfe) { 97 | try { 98 | Instant now = Instant.now(); 99 | Instant retry = LocalDateTime.parse(retryVal).toInstant(ZoneOffset.UTC); 100 | return retry.toEpochMilli() - now.toEpochMilli(); 101 | } catch (DateTimeParseException dtpe) { 102 | throw new RuntimeException("Unable to parse retry time: " + retryVal, dtpe); 103 | } 104 | } 105 | } 106 | 107 | /** 108 | * Helper method to be called recursively in the event a retry is required. 109 | * 110 | * @param client - InfluxDBClient used to make the request. 111 | * @param points - Data to be written. 112 | * @param retryCount - Number of times to retry write requests. 113 | * @throws InterruptedException - if wait is interrupted. 114 | */ 115 | private static void writeWithRetry(final InfluxDBClient client, 116 | final List points, 117 | final int retryCount) throws InterruptedException { 118 | try { 119 | client.writePoints(points); 120 | LOG.info(String.format("Succeeded on write %d\n", (retries - retryCount) + 1)); 121 | } catch (InfluxDBApiHttpException e) { 122 | if ((e.statusCode() == 429 || e.statusCode() == 503) 123 | && e.getHeader("retry-after") != null 124 | && retryCount > 0) { 125 | long wait = resolveRetry(e.getHeader("retry-after").get(0)) + 1000; 126 | LOG.warning( 127 | String.format("Failed to write with HTTP %d waiting %d secs to retry.\n", e.statusCode(), wait / 1000) 128 | ); 129 | keepBusy(wait); 130 | LOG.info(String.format("Write attempt %d", ((retries - (retryCount - 1)) + 1))); 131 | writeWithRetry(client, points, retryCount - 1); 132 | } else { 133 | LOG.severe(String.format("Failed to write in %d tries. Giving up with HTTP %d\n", 134 | retries, 135 | e.statusCode())); 136 | } 137 | } 138 | } 139 | 140 | /** 141 | * Convenience method for a nicer wait experience. 142 | * 143 | * @param wait interval to wait before retrying. 144 | * @throws InterruptedException in the event wait is interrupted. 145 | */ 146 | private static void keepBusy(final long wait) throws InterruptedException { 147 | long ttl = Instant.now().toEpochMilli() + wait; 148 | String[] spinner = {"-", "\\", "|", "/"}; 149 | int count = 0; 150 | while (Instant.now().toEpochMilli() < ttl) { 151 | System.out.printf("\r%s", spinner[count++ % 4]); 152 | System.out.flush(); 153 | TimeUnit.MILLISECONDS.sleep(500); 154 | } 155 | System.out.println(); 156 | } 157 | 158 | /** 159 | * An example data type roughly modeling battery packs in an EV. 160 | */ 161 | static class BatteryPack { 162 | String id; 163 | String vehicle; 164 | int emptyCells; 165 | int cellRack; 166 | double percent; 167 | 168 | public BatteryPack(final String id, 169 | final String vehicle, 170 | final int emptyCells, 171 | final int cellRack, 172 | final double percent) { 173 | this.id = id; 174 | this.vehicle = vehicle; 175 | this.emptyCells = emptyCells; 176 | this.cellRack = cellRack; 177 | this.percent = percent; 178 | } 179 | 180 | public Point toPoint(final Instant time) { 181 | return Point.measurement("bpack") 182 | .setTag("id", id) 183 | .setField("vehicle", vehicle) 184 | .setField("emptyCells", emptyCells) 185 | .setField("totalCells", cellRack) 186 | .setField("percent", percent) 187 | .setTimestamp(time); 188 | } 189 | 190 | static List idSet = List.of(UUID.randomUUID().toString(), 191 | UUID.randomUUID().toString(), 192 | UUID.randomUUID().toString(), 193 | UUID.randomUUID().toString(), 194 | UUID.randomUUID().toString()); 195 | 196 | static List vehicleSet = List.of( 197 | "THX1138", 198 | "VC81072", 199 | "J007O981", 200 | "NTSL1856", 201 | "TOURDF24" 202 | ); 203 | 204 | static List cellsSet = List.of(100, 200, 500); 205 | 206 | public static BatteryPack createRandom() { 207 | int index = (int) Math.floor(Math.random() * 5); 208 | int totalCells = cellsSet.get((int) Math.floor(Math.random() * 3)); 209 | int emptyCells = (int) Math.floor(Math.random() * totalCells); 210 | double percent = ((((double) emptyCells / (double) totalCells)) * 100); 211 | percent += Math.random() * ((100 - percent) * 0.15); 212 | percent = 100 - percent; 213 | return new BatteryPack(idSet.get(index), vehicleSet.get(index), emptyCells, totalCells, percent); 214 | } 215 | 216 | @Override 217 | public String toString() { 218 | return String.format("id: %s, vehicle: %s, cellRack: %d, emptyCells: %d, percent: %f", 219 | id, vehicle, cellRack, emptyCells, percent); 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /license_header.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /maven-version-rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 27 | (?i).*Alpha(?:-?\d+)? 28 | (?i).*a(?:-?\d+)? 29 | (?i).*Beta(?:-?\d+)? 30 | (?i).*-B(?:-?\d+)? 31 | (?i).*RC(?:-?\d+)? 32 | (?i).*CR(?:-?\d+)? 33 | (?i).*M(?:-?\d+)? 34 | (?i).*SNAP(?:-?\d+)? 35 | .*[-_\.](alpha|Alpha|ALPHA|beta|Beta|BETA|rc|‌​RC)[-_\.]?.* 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/InfluxDBApiException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client; 23 | 24 | import javax.annotation.Nullable; 25 | 26 | /** 27 | * The InfluxDBApiException is thrown when an error occurs while interacting with InfluxDB. 28 | */ 29 | public class InfluxDBApiException extends RuntimeException { 30 | /** 31 | * Construct a new InfluxDBApiException with the specified detail message. 32 | * 33 | * @param message the detail message. 34 | */ 35 | public InfluxDBApiException(@Nullable final String message) { 36 | super(message); 37 | } 38 | 39 | /** 40 | * Construct a new InfluxDBApiException with the specified cause. 41 | * 42 | * @param cause the cause. 43 | */ 44 | public InfluxDBApiException(@Nullable final Throwable cause) { 45 | super(cause); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/InfluxDBApiHttpException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client; 23 | 24 | import java.net.http.HttpHeaders; 25 | import java.util.List; 26 | import javax.annotation.Nullable; 27 | 28 | /** 29 | * The InfluxDBApiHttpException gets thrown whenever an error status is returned 30 | * in the HTTP response. It facilitates recovering from such errors whenever possible. 31 | */ 32 | public class InfluxDBApiHttpException extends InfluxDBApiException { 33 | 34 | /** 35 | * The HTTP headers associated with the error. 36 | */ 37 | HttpHeaders headers; 38 | /** 39 | * The HTTP status code associated with the error. 40 | */ 41 | int statusCode; 42 | 43 | /** 44 | * Construct a new InfluxDBApiHttpException with statusCode and headers. 45 | * 46 | * @param message the detail message. 47 | * @param headers headers returned in the response. 48 | * @param statusCode statusCode of the response. 49 | */ 50 | public InfluxDBApiHttpException( 51 | @Nullable final String message, 52 | @Nullable final HttpHeaders headers, 53 | final int statusCode) { 54 | super(message); 55 | this.headers = headers; 56 | this.statusCode = statusCode; 57 | } 58 | 59 | /** 60 | * Construct a new InfluxDBApiHttpException with statusCode and headers. 61 | * 62 | * @param cause root cause of the exception. 63 | * @param headers headers returned in the response. 64 | * @param statusCode status code of the response. 65 | */ 66 | public InfluxDBApiHttpException( 67 | @Nullable final Throwable cause, 68 | @Nullable final HttpHeaders headers, 69 | final int statusCode) { 70 | super(cause); 71 | this.headers = headers; 72 | this.statusCode = statusCode; 73 | } 74 | 75 | /** 76 | * Gets the HTTP headers property associated with the error. 77 | * 78 | * @return - the headers object. 79 | */ 80 | public HttpHeaders headers() { 81 | return headers; 82 | } 83 | 84 | /** 85 | * Helper method to simplify retrieval of specific headers. 86 | * 87 | * @param name - name of the header. 88 | * @return - value matching the header key, or null if the key does not exist. 89 | */ 90 | public List getHeader(final String name) { 91 | return headers.map().get(name); 92 | } 93 | 94 | /** 95 | * Gets the HTTP statusCode associated with the error. 96 | * @return - the HTTP statusCode. 97 | */ 98 | public int statusCode() { 99 | return statusCode; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/internal/Arguments.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import java.time.temporal.ChronoUnit; 25 | import java.util.EnumSet; 26 | import java.util.Objects; 27 | import java.util.regex.Pattern; 28 | import javax.annotation.Nullable; 29 | 30 | /** 31 | * Functions for parameter validation. 32 | *

33 | * Thanks 34 | */ 35 | public final class Arguments { 36 | 37 | private static final Pattern DURATION_PATTERN = Pattern.compile("([-+]?)([0-9]+(\\.[0-9]*)?[a-z]+)+|inf|-inf", 38 | Pattern.CASE_INSENSITIVE); 39 | 40 | private static final String DURATION_MESSAGE = "Expecting a duration string for %s. But got: %s"; 41 | 42 | /** 43 | * The precisions that are allowed to use in the writes. 44 | */ 45 | private static final EnumSet ALLOWED_PRECISION = EnumSet.of(ChronoUnit.NANOS, 46 | ChronoUnit.MICROS, ChronoUnit.MILLIS, ChronoUnit.SECONDS); 47 | 48 | private Arguments() { 49 | } 50 | 51 | /** 52 | * Enforces that the string is {@linkplain String#isEmpty() not empty}. 53 | * 54 | * @param string the string to test 55 | * @param name variable name for reporting 56 | * @return {@code string} 57 | * @throws IllegalArgumentException if the string is empty 58 | */ 59 | public static String checkNonEmpty(final String string, final String name) throws IllegalArgumentException { 60 | if (string == null || string.isEmpty()) { 61 | throw new IllegalArgumentException("Expecting a non-empty string for " + name); 62 | } 63 | return string; 64 | } 65 | 66 | /** 67 | * Enforces that the string has exactly one char. 68 | * 69 | * @param string the string to test 70 | * @param name variable name for reporting 71 | * @return {@code string} 72 | * @throws IllegalArgumentException if the string has not one char 73 | */ 74 | public static String checkOneCharString(final String string, final String name) throws IllegalArgumentException { 75 | if (string == null || string.length() != 1) { 76 | throw new IllegalArgumentException("Expecting a one char string for " + name); 77 | } 78 | return string; 79 | } 80 | 81 | /** 82 | * Enforces that the string is duration literal. 83 | * 84 | * @param string the string to test 85 | * @param name variable name for reporting 86 | * @return {@code string} 87 | * @throws IllegalArgumentException if the string is not duration literal 88 | */ 89 | public static String checkDuration(final String string, final String name) throws IllegalArgumentException { 90 | if (string == null || string.isEmpty() || !DURATION_PATTERN.matcher(string).matches()) { 91 | throw new IllegalArgumentException(String.format(DURATION_MESSAGE, name, string)); 92 | } 93 | 94 | return string; 95 | } 96 | 97 | /** 98 | * Enforces that the string is duration literal. Empty or null strings are valid. 99 | * 100 | * @param string the string to test 101 | * @param name variable name for reporting 102 | * @return {@code string} 103 | * @throws IllegalArgumentException if the string is not duration literal 104 | */ 105 | public static String checkDurationNotRequired(final String string, final String name) 106 | throws IllegalArgumentException { 107 | if (string != null && !string.isEmpty() && !DURATION_PATTERN.matcher(string).matches()) { 108 | throw new IllegalArgumentException(String.format(DURATION_MESSAGE, name, string)); 109 | } 110 | 111 | return string; 112 | } 113 | 114 | /** 115 | * Enforces that the number is larger than 0. 116 | * 117 | * @param number the number to test 118 | * @param name variable name for reporting 119 | * @throws IllegalArgumentException if the number is less or equal to 0 120 | */ 121 | public static void checkPositiveNumber(final Number number, final String name) throws IllegalArgumentException { 122 | if (number == null || number.doubleValue() <= 0) { 123 | throw new IllegalArgumentException("Expecting a positive number for " + name); 124 | } 125 | } 126 | 127 | /** 128 | * Enforces that the number is not negative. 129 | * 130 | * @param number the number to test 131 | * @param name variable name for reporting 132 | * @throws IllegalArgumentException if the number is less or equal to 0 133 | */ 134 | public static void checkNotNegativeNumber(final Number number, final String name) throws IllegalArgumentException { 135 | if (number == null || number.doubleValue() < 0) { 136 | throw new IllegalArgumentException("Expecting a positive or zero number for " + name); 137 | } 138 | } 139 | 140 | /** 141 | * Checks that the specified object reference is not {@code null}. 142 | * 143 | * @param obj the object reference to check for nullity 144 | * @param name variable name for reporting 145 | * @throws NullPointerException if the object is {@code null} 146 | * @see Objects#requireNonNull(Object, String) 147 | */ 148 | public static void checkNotNull(final Object obj, final String name) throws NullPointerException { 149 | 150 | Objects.requireNonNull(obj, () -> "Expecting a not null reference for " + name); 151 | } 152 | 153 | /** 154 | * Checks that the precision reference to one of {@link Arguments#ALLOWED_PRECISION}. 155 | * 156 | * @param precision the precision to check 157 | * @throws IllegalArgumentException if the object is not one of {@link Arguments#ALLOWED_PRECISION} 158 | */ 159 | public static void checkPrecision(@Nullable final ChronoUnit precision) throws IllegalArgumentException { 160 | 161 | if (!ALLOWED_PRECISION.contains(precision)) { 162 | throw new IllegalArgumentException("Precision must be one of: " + ALLOWED_PRECISION); 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/internal/FlightSqlClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import java.io.FileInputStream; 25 | import java.net.InetSocketAddress; 26 | import java.net.URI; 27 | import java.net.URISyntaxException; 28 | import java.nio.charset.StandardCharsets; 29 | import java.util.ArrayList; 30 | import java.util.HashMap; 31 | import java.util.Iterator; 32 | import java.util.List; 33 | import java.util.Map; 34 | import java.util.NoSuchElementException; 35 | import java.util.Spliterator; 36 | import java.util.Spliterators; 37 | import java.util.stream.Stream; 38 | import java.util.stream.StreamSupport; 39 | import javax.annotation.Nonnull; 40 | import javax.annotation.Nullable; 41 | 42 | import com.fasterxml.jackson.core.JsonProcessingException; 43 | import com.fasterxml.jackson.databind.ObjectMapper; 44 | import io.grpc.HttpConnectProxiedSocketAddress; 45 | import io.grpc.Metadata; 46 | import io.grpc.ProxyDetector; 47 | import io.grpc.netty.GrpcSslContexts; 48 | import io.grpc.netty.NettyChannelBuilder; 49 | import io.netty.handler.ssl.SslContext; 50 | import io.netty.handler.ssl.SslContextBuilder; 51 | import io.netty.handler.ssl.util.InsecureTrustManagerFactory; 52 | import org.apache.arrow.flight.CallOption; 53 | import org.apache.arrow.flight.FlightClient; 54 | import org.apache.arrow.flight.FlightGrpcUtils; 55 | import org.apache.arrow.flight.FlightStream; 56 | import org.apache.arrow.flight.HeaderCallOption; 57 | import org.apache.arrow.flight.Location; 58 | import org.apache.arrow.flight.LocationSchemes; 59 | import org.apache.arrow.flight.Ticket; 60 | import org.apache.arrow.flight.grpc.MetadataAdapter; 61 | import org.apache.arrow.memory.RootAllocator; 62 | import org.apache.arrow.util.AutoCloseables; 63 | import org.apache.arrow.vector.VectorSchemaRoot; 64 | import org.slf4j.Logger; 65 | import org.slf4j.LoggerFactory; 66 | 67 | import com.influxdb.v3.client.config.ClientConfig; 68 | import com.influxdb.v3.client.query.QueryType; 69 | 70 | final class FlightSqlClient implements AutoCloseable { 71 | 72 | private static final Logger LOG = LoggerFactory.getLogger(FlightSqlClient.class); 73 | 74 | private final FlightClient client; 75 | 76 | private final Map defaultHeaders = new HashMap<>(); 77 | private final ObjectMapper objectMapper = new ObjectMapper(); 78 | 79 | FlightSqlClient(@Nonnull final ClientConfig config) { 80 | this(config, null); 81 | } 82 | 83 | /** 84 | * Constructor for testing purposes. 85 | * 86 | * @param config the client configuration 87 | * @param client the flight client, if null a new client will be created 88 | */ 89 | FlightSqlClient(@Nonnull final ClientConfig config, @Nullable final FlightClient client) { 90 | Arguments.checkNotNull(config, "config"); 91 | 92 | if (config.getToken() != null && config.getToken().length > 0) { 93 | defaultHeaders.put("Authorization", "Bearer " + new String(config.getToken())); 94 | } 95 | 96 | defaultHeaders.put("User-Agent", Identity.getUserAgent()); 97 | 98 | if (config.getHeaders() != null) { 99 | defaultHeaders.putAll(config.getHeaders()); 100 | } 101 | 102 | this.client = (client != null) ? client : createFlightClient(config); 103 | } 104 | 105 | @Nonnull 106 | Stream execute(@Nonnull final String query, 107 | @Nonnull final String database, 108 | @Nonnull final QueryType queryType, 109 | @Nonnull final Map queryParameters, 110 | @Nonnull final Map headers, 111 | final CallOption... callOptions) { 112 | 113 | Map ticketData = new HashMap<>() {{ 114 | put("database", database); 115 | put("sql_query", query); 116 | put("query_type", queryType.name().toLowerCase()); 117 | }}; 118 | 119 | if (!queryParameters.isEmpty()) { 120 | ticketData.put("params", queryParameters); 121 | } 122 | 123 | String json; 124 | try { 125 | json = objectMapper.writeValueAsString(ticketData); 126 | } catch (JsonProcessingException e) { 127 | throw new RuntimeException(e); 128 | } 129 | 130 | HeaderCallOption headerCallOption = metadataHeader(headers); 131 | CallOption[] callOptionArray = GrpcCallOptions.mergeCallOptions(callOptions, headerCallOption); 132 | 133 | Ticket ticket = new Ticket(json.getBytes(StandardCharsets.UTF_8)); 134 | FlightStream stream = client.getStream(ticket, callOptionArray); 135 | FlightSqlIterator iterator = new FlightSqlIterator(stream); 136 | 137 | Spliterator spliterator = Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL); 138 | return StreamSupport.stream(spliterator, false).onClose(iterator::close); 139 | } 140 | 141 | @Override 142 | public void close() throws Exception { 143 | client.close(); 144 | } 145 | 146 | @Nonnull 147 | private FlightClient createFlightClient(@Nonnull final ClientConfig config) { 148 | URI uri = createLocation(config).getUri(); 149 | final NettyChannelBuilder nettyChannelBuilder = NettyChannelBuilder.forAddress(uri.getHost(), uri.getPort()); 150 | 151 | if (LocationSchemes.GRPC_TLS.equals(uri.getScheme())) { 152 | nettyChannelBuilder.useTransportSecurity(); 153 | 154 | SslContext nettySslContext = createNettySslContext(config); 155 | nettyChannelBuilder.sslContext(nettySslContext); 156 | } else { 157 | nettyChannelBuilder.usePlaintext(); 158 | } 159 | 160 | if (config.getProxyUrl() != null) { 161 | ProxyDetector proxyDetector = createProxyDetector(config.getHost(), config.getProxyUrl()); 162 | nettyChannelBuilder.proxyDetector(proxyDetector); 163 | } 164 | 165 | if (config.getProxy() != null) { 166 | LOG.warn("proxy property in ClientConfig will not work in query api, use proxyUrl property instead"); 167 | } 168 | 169 | nettyChannelBuilder.maxTraceEvents(0) 170 | .maxInboundMetadataSize(Integer.MAX_VALUE); 171 | 172 | return FlightGrpcUtils.createFlightClient(new RootAllocator(Long.MAX_VALUE), nettyChannelBuilder.build()); 173 | } 174 | 175 | @Nonnull 176 | SslContext createNettySslContext(@Nonnull final ClientConfig config) { 177 | try { 178 | SslContextBuilder sslContextBuilder; 179 | sslContextBuilder = GrpcSslContexts.forClient(); 180 | if (config.getDisableServerCertificateValidation()) { 181 | sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE); 182 | } else if (config.sslRootsFilePath() != null) { 183 | try (FileInputStream fileInputStream = new FileInputStream(config.sslRootsFilePath())) { 184 | sslContextBuilder.trustManager(fileInputStream); 185 | } 186 | } 187 | return sslContextBuilder.build(); 188 | } catch (Exception e) { 189 | throw new RuntimeException(e); 190 | } 191 | } 192 | 193 | @Nonnull 194 | private Location createLocation(@Nonnull final ClientConfig config) { 195 | try { 196 | URI uri = new URI(config.getHost()); 197 | if ("https".equals(uri.getScheme())) { 198 | return Location.forGrpcTls(uri.getHost(), uri.getPort() != -1 ? uri.getPort() : 443); 199 | } else { 200 | return Location.forGrpcInsecure(uri.getHost(), uri.getPort() != -1 ? uri.getPort() : 80); 201 | } 202 | } catch (URISyntaxException e) { 203 | throw new RuntimeException(e); 204 | } 205 | } 206 | 207 | @Nonnull 208 | private HeaderCallOption metadataHeader(@Nonnull final Map requestHeaders) { 209 | MetadataAdapter metadata = new MetadataAdapter(new Metadata()); 210 | for (Map.Entry entry : requestHeaders.entrySet()) { 211 | metadata.insert(entry.getKey(), entry.getValue()); 212 | } 213 | 214 | for (Map.Entry entry : defaultHeaders.entrySet()) { 215 | if (!metadata.containsKey(entry.getKey())) { 216 | metadata.insert(entry.getKey(), entry.getValue()); 217 | } 218 | } 219 | return new HeaderCallOption(metadata); 220 | } 221 | 222 | ProxyDetector createProxyDetector(@Nonnull final String targetUrl, @Nonnull final String proxyUrl) { 223 | URI targetUri = URI.create(targetUrl); 224 | URI proxyUri = URI.create(proxyUrl); 225 | return (targetServerAddress) -> { 226 | InetSocketAddress targetAddress = (InetSocketAddress) targetServerAddress; 227 | if (targetUri.getHost().equals(targetAddress.getHostString()) 228 | && targetUri.getPort() == targetAddress.getPort()) { 229 | return HttpConnectProxiedSocketAddress.newBuilder() 230 | .setProxyAddress(new InetSocketAddress(proxyUri.getHost(), proxyUri.getPort())) 231 | .setTargetAddress(targetAddress) 232 | .build(); 233 | } 234 | return null; 235 | }; 236 | } 237 | 238 | private static final class FlightSqlIterator implements Iterator, AutoCloseable { 239 | 240 | private final List autoCloseable = new ArrayList<>(); 241 | 242 | private final FlightStream flightStream; 243 | 244 | private FlightSqlIterator(@Nonnull final FlightStream flightStream) { 245 | this.flightStream = flightStream; 246 | } 247 | 248 | @Override 249 | public boolean hasNext() { 250 | return flightStream.next(); 251 | } 252 | 253 | @Override 254 | public VectorSchemaRoot next() { 255 | if (flightStream.getRoot() == null) { 256 | throw new NoSuchElementException(); 257 | } 258 | 259 | autoCloseable.add(flightStream.getRoot()); 260 | 261 | return flightStream.getRoot(); 262 | } 263 | 264 | @Override 265 | public void close() { 266 | try { 267 | AutoCloseables.close(autoCloseable); 268 | } catch (Exception e) { 269 | throw new RuntimeException(e); 270 | } 271 | } 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/internal/Identity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | /** 25 | * Functions for establishing caller identity. 26 | */ 27 | final class Identity { 28 | private Identity() { } 29 | 30 | /** 31 | * Attempt to get the package version. 32 | * @return - package version or unknown. 33 | */ 34 | static String getVersion() { 35 | Package mainPackage = Identity.class.getPackage(); 36 | String version = mainPackage != null ? mainPackage.getImplementationVersion() : "unknown"; 37 | return version == null ? "unknown" : version; 38 | } 39 | 40 | /** 41 | * Get a standard user-agent identity to be used in all HTTP based calls. 42 | * @return - the standard user-agent string. 43 | */ 44 | static String getUserAgent() { 45 | return String.format("influxdb3-java/%s", getVersion()); 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/internal/NanosecondConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import java.math.BigDecimal; 25 | import java.math.BigInteger; 26 | import java.time.Instant; 27 | import java.time.LocalDateTime; 28 | import java.time.ZoneOffset; 29 | import java.util.HashMap; 30 | import java.util.Map; 31 | import java.util.concurrent.TimeUnit; 32 | import java.util.function.Function; 33 | import javax.annotation.Nonnull; 34 | import javax.annotation.Nullable; 35 | 36 | import org.apache.arrow.vector.types.pojo.ArrowType; 37 | import org.apache.arrow.vector.types.pojo.Field; 38 | 39 | import com.influxdb.v3.client.write.WritePrecision; 40 | 41 | import static java.util.function.Function.identity; 42 | 43 | /** 44 | * Nanosecond converter. 45 | *

46 | * Utility class converting epoch nanoseconds to epoch with a given precision. 47 | */ 48 | public final class NanosecondConverter { 49 | 50 | private static final BigInteger NANOS_PER_SECOND = BigInteger.valueOf(1000_000_000L); 51 | private static final BigInteger MICRO_PER_NANOS = BigInteger.valueOf(1000L); 52 | private static final BigInteger MILLIS_PER_NANOS = BigInteger.valueOf(1000_000L); 53 | private static final BigInteger SECONDS_PER_NANOS = BigInteger.valueOf(1000_000_000L); 54 | 55 | private NanosecondConverter() { 56 | } 57 | 58 | /** 59 | * Timestamp calculation functions to add timestamp to records. 60 | */ 61 | private static final Map> FROM_NANOS = new HashMap<>(); 62 | 63 | static { 64 | FROM_NANOS.put(WritePrecision.S, (timestamp) -> timestamp.divide(SECONDS_PER_NANOS)); 65 | FROM_NANOS.put(WritePrecision.MS, (timestamp) -> timestamp.divide(MILLIS_PER_NANOS)); 66 | FROM_NANOS.put(WritePrecision.US, (timestamp) -> timestamp.divide(MICRO_PER_NANOS)); 67 | FROM_NANOS.put(WritePrecision.NS, identity()); 68 | } 69 | 70 | /** 71 | * Timestamp calculation functions from specified precision to nanos. 72 | */ 73 | private static final Map> TO_NANOS = new HashMap<>(); 74 | 75 | static { 76 | TO_NANOS.put(WritePrecision.S, (timestamp) -> timestamp.multiply(SECONDS_PER_NANOS)); 77 | TO_NANOS.put(WritePrecision.MS, (timestamp) -> timestamp.multiply(MILLIS_PER_NANOS)); 78 | TO_NANOS.put(WritePrecision.US, (timestamp) -> timestamp.multiply(MICRO_PER_NANOS)); 79 | TO_NANOS.put(WritePrecision.NS, identity()); 80 | } 81 | 82 | /** 83 | * Convert timestamp in a given precision to nanoseconds. 84 | * 85 | * @param timestamp epoch timestamp 86 | * @param precision precision 87 | * @return epoch timestamp in precision, can be null 88 | */ 89 | @Nullable 90 | public static BigInteger convertToNanos(@Nullable final Number timestamp, final WritePrecision precision) { 91 | if (timestamp == null) { 92 | return null; 93 | } 94 | 95 | BigInteger t; 96 | if (timestamp instanceof BigDecimal) { 97 | t = ((BigDecimal) timestamp).toBigInteger(); 98 | } else if (timestamp instanceof BigInteger) { 99 | t = (BigInteger) timestamp; 100 | } else { 101 | t = BigInteger.valueOf(timestamp.longValue()); 102 | } 103 | 104 | return TO_NANOS.get(precision).apply(t); 105 | } 106 | 107 | /** 108 | * Convert {@link Instant} timestamp to a given precision. 109 | * 110 | * @param instant Instant timestamp 111 | * @param precision precision 112 | * @return epoch timestamp in precision 113 | */ 114 | public static BigInteger convert(final Instant instant, final WritePrecision precision) { 115 | BigInteger nanos = BigInteger.valueOf(instant.getEpochSecond()) 116 | .multiply(NANOS_PER_SECOND) 117 | .add(BigInteger.valueOf(instant.getNano())); 118 | 119 | return FROM_NANOS.get(precision).apply(nanos); 120 | } 121 | 122 | /** 123 | * Convert Long or LocalDateTime to timestamp nanosecond. 124 | * 125 | * @param value the time in Long or LocalDateTime 126 | * @param field the arrow field metadata 127 | * @return the time in nanosecond 128 | */ 129 | @Nullable 130 | public static BigInteger getTimestampNano(@Nonnull final Object value, @Nonnull final Field field) { 131 | BigInteger result = null; 132 | 133 | if (value instanceof Long) { 134 | if (field.getFieldType().getType() instanceof ArrowType.Timestamp) { 135 | ArrowType.Timestamp type = (ArrowType.Timestamp) field.getFieldType().getType(); 136 | TimeUnit timeUnit; 137 | switch (type.getUnit()) { 138 | case SECOND: 139 | timeUnit = TimeUnit.SECONDS; 140 | break; 141 | case MILLISECOND: 142 | timeUnit = TimeUnit.MILLISECONDS; 143 | break; 144 | case MICROSECOND: 145 | timeUnit = TimeUnit.MICROSECONDS; 146 | break; 147 | case NANOSECOND: 148 | default: 149 | timeUnit = TimeUnit.NANOSECONDS; 150 | break; 151 | } 152 | long nanoseconds = TimeUnit.NANOSECONDS.convert((Long) value, timeUnit); 153 | Instant instant = Instant.ofEpochSecond(0, nanoseconds); 154 | result = convertInstantToNano(instant); 155 | } else { 156 | Instant instant = Instant.ofEpochMilli((Long) value); 157 | result = convertInstantToNano(instant); 158 | } 159 | } else if (value instanceof LocalDateTime) { 160 | Instant instant = ((LocalDateTime) value).toInstant(ZoneOffset.UTC); 161 | result = convertInstantToNano(instant); 162 | } 163 | return result; 164 | } 165 | 166 | @Nullable 167 | private static BigInteger convertInstantToNano(@Nonnull final Instant instant) { 168 | var writePrecision = WritePrecision.NS; 169 | BigInteger convertedTime = NanosecondConverter.convert(instant, writePrecision); 170 | return NanosecondConverter.convertToNanos(convertedTime, writePrecision); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/internal/TypeCasting.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import javax.annotation.Nonnull; 25 | 26 | import org.apache.arrow.vector.util.Text; 27 | 28 | /** 29 | * Functions for safe type casting. 30 | */ 31 | public final class TypeCasting { 32 | 33 | private TypeCasting() { } 34 | 35 | /** 36 | * Safe casting to long value. 37 | * 38 | * @param value object to cast 39 | * @return long value 40 | */ 41 | public static long toLongValue(@Nonnull final Object value) { 42 | 43 | if (long.class.isAssignableFrom(value.getClass()) 44 | || Long.class.isAssignableFrom(value.getClass())) { 45 | return (long) value; 46 | } 47 | 48 | return ((Number) value).longValue(); 49 | } 50 | 51 | /** 52 | * Safe casting to double value. 53 | * 54 | * @param value object to cast 55 | * @return double value 56 | */ 57 | public static double toDoubleValue(@Nonnull final Object value) { 58 | 59 | if (double.class.isAssignableFrom(value.getClass()) 60 | || Double.class.isAssignableFrom(value.getClass())) { 61 | return (double) value; 62 | } 63 | 64 | return ((Number) value).doubleValue(); 65 | } 66 | 67 | /** 68 | * Safe casting to string value. 69 | * 70 | * @param value object to cast 71 | * @return string value 72 | */ 73 | public static String toStringValue(@Nonnull final Object value) { 74 | 75 | if (Text.class.isAssignableFrom(value.getClass())) { 76 | return value.toString(); 77 | } 78 | 79 | return (String) value; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/internal/VectorSchemaRootConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import java.math.BigInteger; 25 | import java.time.LocalDateTime; 26 | import java.util.LinkedHashMap; 27 | import java.util.List; 28 | import java.util.Map; 29 | import java.util.Objects; 30 | import java.util.logging.Logger; 31 | import javax.annotation.Nonnull; 32 | import javax.annotation.Nullable; 33 | import javax.annotation.concurrent.ThreadSafe; 34 | 35 | import org.apache.arrow.vector.FieldVector; 36 | import org.apache.arrow.vector.VectorSchemaRoot; 37 | import org.apache.arrow.vector.types.pojo.Field; 38 | import org.apache.arrow.vector.util.Text; 39 | 40 | import com.influxdb.v3.client.PointValues; 41 | import com.influxdb.v3.client.write.WritePrecision; 42 | 43 | 44 | /** 45 | * The VectorSchemaRootConverter class is responsible for converting rows of data from a VectorSchemaRoot object 46 | * to PointValues. It provides a method to perform this conversion. 47 | *

48 | * This class is thread-safe. 49 | */ 50 | @ThreadSafe 51 | public final class VectorSchemaRootConverter { 52 | 53 | private static final Logger LOG = Logger.getLogger(VectorSchemaRootConverter.class.getName()); 54 | 55 | public static final VectorSchemaRootConverter INSTANCE = new VectorSchemaRootConverter(); 56 | 57 | /** 58 | * Converts a given row of data from a VectorSchemaRoot object to PointValues. 59 | * 60 | * @param rowNumber the index of the row to be converted 61 | * @param fieldVectors the list of FieldVector objects representing the data columns 62 | * @return the converted PointValues object 63 | */ 64 | @Nonnull 65 | PointValues toPointValues(final int rowNumber, 66 | @Nonnull final List fieldVectors) { 67 | PointValues p = new PointValues(); 68 | for (FieldVector fieldVector : fieldVectors) { 69 | var field = fieldVector.getField(); 70 | var value = fieldVector.getObject(rowNumber); 71 | var fieldName = field.getName(); 72 | var metaType = field.getMetadata().get("iox::column::type"); 73 | 74 | if (value instanceof Text) { 75 | value = value.toString(); 76 | } 77 | 78 | if ((Objects.equals(fieldName, "measurement") 79 | || Objects.equals(fieldName, "iox::measurement")) 80 | && value instanceof String) { 81 | p.setMeasurement((String) value); 82 | continue; 83 | } 84 | 85 | if (metaType == null) { 86 | if (Objects.equals(fieldName, "time") && (value instanceof Long || value instanceof LocalDateTime)) { 87 | var timeNano = NanosecondConverter.getTimestampNano(value, field); 88 | p.setTimestamp(timeNano, WritePrecision.NS); 89 | } else { 90 | // just push as field If you don't know what type is it 91 | p.setField(fieldName, value); 92 | } 93 | 94 | continue; 95 | } 96 | 97 | String valueType = metaType.split("::")[2]; 98 | Object mappedValue = getMappedValue(field, value); 99 | if ("field".equals(valueType)) { 100 | p.setField(fieldName, mappedValue); 101 | } else if ("tag".equals(valueType) && value instanceof String) { 102 | p.setTag(fieldName, (String) mappedValue); 103 | } else if ("timestamp".equals(valueType)) { 104 | p.setTimestamp((BigInteger) mappedValue, WritePrecision.NS); 105 | } 106 | } 107 | return p; 108 | } 109 | 110 | /** 111 | * Function to cast value return base on metadata from InfluxDB. 112 | * 113 | * @param field the Field object from Arrow 114 | * @param value the value to cast 115 | * @return the value with the correct type 116 | */ 117 | public Object getMappedValue(@Nonnull final Field field, @Nullable final Object value) { 118 | if (value == null) { 119 | return null; 120 | } 121 | 122 | var fieldName = field.getName(); 123 | if ("measurement".equals(fieldName) || "iox::measurement".equals(fieldName)) { 124 | return value.toString(); 125 | } 126 | 127 | var metaType = field.getMetadata().get("iox::column::type"); 128 | if (metaType == null) { 129 | if ("time".equals(fieldName) && (value instanceof Long || value instanceof LocalDateTime)) { 130 | return NanosecondConverter.getTimestampNano(value, field); 131 | } else { 132 | return value; 133 | } 134 | } 135 | 136 | String[] parts = metaType.split("::"); 137 | String valueType = parts[2]; 138 | if ("field".equals(valueType)) { 139 | switch (metaType) { 140 | case "iox::column_type::field::integer": 141 | case "iox::column_type::field::uinteger": 142 | if (value instanceof Number) { 143 | return TypeCasting.toLongValue(value); 144 | } else { 145 | LOG.warning(String.format("Value %s is not an Long", value)); 146 | return value; 147 | } 148 | case "iox::column_type::field::float": 149 | if (value instanceof Number) { 150 | return TypeCasting.toDoubleValue(value); 151 | } else { 152 | LOG.warning(String.format("Value %s is not a Double", value)); 153 | return value; 154 | } 155 | case "iox::column_type::field::string": 156 | if (value instanceof Text || value instanceof String) { 157 | return TypeCasting.toStringValue(value); 158 | } else { 159 | LOG.warning(String.format("Value %s is not a String", value)); 160 | return value; 161 | } 162 | case "iox::column_type::field::boolean": 163 | if (value instanceof Boolean) { 164 | return value; 165 | } else { 166 | LOG.warning(String.format("Value %s is not a Boolean", value)); 167 | return value; 168 | } 169 | default: 170 | return value; 171 | } 172 | } else if ("timestamp".equals(valueType) || Objects.equals(fieldName, "time")) { 173 | return NanosecondConverter.getTimestampNano(value, field); 174 | } else { 175 | return TypeCasting.toStringValue(value); 176 | } 177 | } 178 | 179 | /** 180 | * Get array of values from VectorSchemaRoot. 181 | * 182 | * @param vector The data return from InfluxDB. 183 | * @param rowNumber The row number of data 184 | * @return An array of Objects represents a row of data 185 | */ 186 | public Object[] getArrayObjectFromVectorSchemaRoot(@Nonnull final VectorSchemaRoot vector, final int rowNumber) { 187 | List fieldVectors = vector.getFieldVectors(); 188 | int columnSize = fieldVectors.size(); 189 | var row = new Object[columnSize]; 190 | for (int i = 0; i < columnSize; i++) { 191 | FieldVector fieldVector = fieldVectors.get(i); 192 | row[i] = getMappedValue( 193 | fieldVector.getField(), 194 | fieldVector.getObject(rowNumber) 195 | ); 196 | } 197 | 198 | return row; 199 | } 200 | 201 | /** 202 | * Get a Map from VectorSchemaRoot. 203 | * 204 | * @param vector The data return from InfluxDB. 205 | * @param rowNumber The row number of data 206 | * @return A Map represents a row of data 207 | */ 208 | public Map getMapFromVectorSchemaRoot(@Nonnull final VectorSchemaRoot vector, final int rowNumber) { 209 | Map row = new LinkedHashMap<>(); 210 | for (FieldVector fieldVector : vector.getFieldVectors()) { 211 | Object mappedValue = getMappedValue( 212 | fieldVector.getField(), 213 | fieldVector.getObject(rowNumber) 214 | ); 215 | row.put(fieldVector.getName(), mappedValue); 216 | 217 | } 218 | 219 | return row; 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/query/QueryOptions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.query; 23 | 24 | import java.util.Map; 25 | import javax.annotation.Nonnull; 26 | import javax.annotation.Nullable; 27 | import javax.annotation.concurrent.ThreadSafe; 28 | 29 | import com.influxdb.v3.client.config.ClientConfig; 30 | import com.influxdb.v3.client.internal.Arguments; 31 | import com.influxdb.v3.client.internal.GrpcCallOptions; 32 | 33 | /** 34 | * Query API options. 35 | *

36 | * Supports to specify: 37 | *

    38 | *
  • database - specifies the database to be used for InfluxDB operations
  • 39 | *
  • queryType - specifies the type of query sent to InfluxDB. Default to 'SQL'.
  • 40 | *
  • headers - specifies the headers to be added to query request
  • 41 | *
42 | *

43 | * To add custom headers to the query request, use the following code: 44 | *

 45 |  * QueryOptions options = new QueryOptions(Map.of("X-Tracing-Id", "123"));
 46 |  * Stream<Object[]> rows = client.query("select * from cpu", queryOptions);
 47 |  * 
48 | */ 49 | @ThreadSafe 50 | @SuppressWarnings("ConstantConditions") 51 | public final class QueryOptions { 52 | 53 | /** 54 | * Default QueryAPI options. 55 | */ 56 | public static final QueryOptions DEFAULTS = new QueryOptions(null, QueryType.SQL); 57 | /** 58 | * Default QueryAPI options for InfluxQL. 59 | */ 60 | public static final QueryOptions INFLUX_QL = new QueryOptions(null, QueryType.InfluxQL); 61 | 62 | private final String database; 63 | private final QueryType queryType; 64 | private final Map headers; 65 | private GrpcCallOptions grpcCallOptions = GrpcCallOptions.getDefaultOptions(); 66 | 67 | /** 68 | * Construct QueryAPI options. The query type is set to SQL. 69 | * 70 | * @param database The database to be used for InfluxDB operations. 71 | */ 72 | public QueryOptions(@Nonnull final String database) { 73 | this(database, QueryType.SQL); 74 | } 75 | 76 | /** 77 | * Construct QueryAPI options. 78 | * 79 | * @param queryType The type of query sent to InfluxDB. 80 | */ 81 | public QueryOptions(@Nonnull final QueryType queryType) { 82 | this(null, queryType); 83 | } 84 | 85 | /** 86 | * Construct QueryAPI options. The query type is set to SQL. 87 | * 88 | * @param headers The headers to be added to query request. 89 | * The headers specified here are preferred over the headers specified in the client configuration. 90 | */ 91 | public QueryOptions(@Nullable final Map headers) { 92 | this(null, QueryType.SQL, headers); 93 | } 94 | 95 | /** 96 | * Construct QueryAPI options. 97 | * 98 | * @param database The database to be used for InfluxDB operations. 99 | * If it is not specified then use {@link ClientConfig#getDatabase()}. 100 | * @param queryType The type of query sent to InfluxDB. If it is not specified then use {@link QueryType#SQL}. 101 | */ 102 | public QueryOptions(@Nullable final String database, @Nullable final QueryType queryType) { 103 | this(database, queryType, null); 104 | } 105 | 106 | /** 107 | * Construct QueryAPI options. 108 | * 109 | * @param database The database to be used for InfluxDB operations. 110 | * If it is not specified then use {@link ClientConfig#getDatabase()}. 111 | * @param queryType The type of query sent to InfluxDB. If it is not specified then use {@link QueryType#SQL}. 112 | * @param headers The headers to be added to query request. 113 | * The headers specified here are preferred over the headers specified in the client configuration. 114 | */ 115 | public QueryOptions(@Nullable final String database, 116 | @Nullable final QueryType queryType, 117 | @Nullable final Map headers) { 118 | this.database = database; 119 | this.queryType = queryType; 120 | this.headers = headers == null ? Map.of() : headers; 121 | } 122 | 123 | /** 124 | * @param config with default value 125 | * @return The destination database for writes. 126 | */ 127 | @Nullable 128 | public String databaseSafe(@Nonnull final ClientConfig config) { 129 | Arguments.checkNotNull(config, "config"); 130 | return isNotDefined(database) ? config.getDatabase() : database; 131 | } 132 | 133 | /** 134 | * @return The type of query sent to InfluxDB, cannot be null. 135 | */ 136 | @Nonnull 137 | public QueryType queryTypeSafe() { 138 | return queryType == null ? QueryType.SQL : queryType; 139 | } 140 | 141 | /** 142 | * @return The headers to be added to query request, cannot be null. 143 | */ 144 | @Nonnull 145 | public Map headersSafe() { 146 | return headers; 147 | } 148 | 149 | /** 150 | * Sets the GrpcCallOptions object. 151 | * @param grpcCallOptions the grpcCallOptions 152 | */ 153 | public void setGrpcCallOptions(@Nonnull final GrpcCallOptions grpcCallOptions) { 154 | Arguments.checkNotNull(grpcCallOptions, "grpcCallOptions"); 155 | this.grpcCallOptions = grpcCallOptions; 156 | } 157 | 158 | /** 159 | * @return the GrpcCallOptions object. 160 | */ 161 | @Nonnull 162 | public GrpcCallOptions grpcCallOptions() { 163 | return grpcCallOptions; 164 | } 165 | 166 | private boolean isNotDefined(final String option) { 167 | return option == null || option.isEmpty(); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/query/QueryType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.query; 23 | 24 | /** 25 | * Defines type of query sent to InfluxDB. 26 | */ 27 | public enum QueryType { 28 | 29 | /** 30 | * Query by SQL. 31 | */ 32 | SQL, 33 | 34 | /** 35 | * Query by InfluxQL. 36 | */ 37 | InfluxQL 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/write/WritePrecision.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.write; 23 | 24 | /** 25 | * Defines WritePrecision. 26 | */ 27 | public enum WritePrecision { 28 | 29 | /** 30 | * Time precision in milliseconds. 31 | */ 32 | MS, 33 | /** 34 | * Time precision in seconds. 35 | */ 36 | S, 37 | /** 38 | * Time precision in microseconds. 39 | */ 40 | US, 41 | /** 42 | * Time precision in nanoseconds. 43 | */ 44 | NS 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/write/WritePrecisionConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.write; 23 | 24 | public final class WritePrecisionConverter { 25 | 26 | private WritePrecisionConverter() { 27 | } 28 | 29 | public static String toV2ApiString(final WritePrecision precision) { 30 | switch (precision) { 31 | case NS: 32 | return "ns"; 33 | case US: 34 | return "us"; 35 | case MS: 36 | return "ms"; 37 | case S: 38 | return "s"; 39 | default: 40 | throw new IllegalArgumentException("Unsupported precision '" + precision + "'"); 41 | } 42 | } 43 | 44 | public static String toV3ApiString(final WritePrecision precision) { 45 | switch (precision) { 46 | case NS: 47 | return "nanosecond"; 48 | case US: 49 | return "microsecond"; 50 | case MS: 51 | return "millisecond"; 52 | case S: 53 | return "second"; 54 | default: 55 | throw new IllegalArgumentException("Unsupported precision '" + precision + "'"); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /src/site/site.xml: -------------------------------------------------------------------------------- 1 | 24 | 26 | 27 | ${project.name} 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.apache.maven.skins 50 | maven-fluido-skin 51 | 1.7 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/AbstractMockServerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client; 23 | 24 | import java.io.IOException; 25 | import java.util.Map; 26 | import javax.annotation.Nonnull; 27 | 28 | import okhttp3.mockwebserver.MockResponse; 29 | import okhttp3.mockwebserver.MockWebServer; 30 | import org.junit.jupiter.api.AfterEach; 31 | import org.junit.jupiter.api.BeforeEach; 32 | 33 | public abstract class AbstractMockServerTest { 34 | 35 | protected String baseURL; 36 | protected MockWebServer mockServer; 37 | 38 | @BeforeEach 39 | protected void startMockServer() { 40 | 41 | mockServer = new MockWebServer(); 42 | try { 43 | mockServer.start(); 44 | } catch (IOException e) { 45 | throw new RuntimeException(e); 46 | } 47 | 48 | baseURL = mockServer.url("/").url().toString(); 49 | } 50 | 51 | @AfterEach 52 | protected void shutdownMockServer() throws IOException { 53 | mockServer.shutdown(); 54 | } 55 | 56 | @Nonnull 57 | protected MockResponse createEmptyResponse(final int responseCode) { 58 | return new MockResponse().setResponseCode(responseCode); 59 | } 60 | 61 | @Nonnull 62 | protected MockResponse createResponse(final int responseCode) { 63 | 64 | return createResponseWithHeaders(responseCode, Map.of( 65 | "Content-Type", "text/csv; charset=utf-8", 66 | "Date", "Tue, 26 Jun 2018 13:15:01 GMT" 67 | )); 68 | } 69 | 70 | @Nonnull 71 | protected MockResponse createResponseWithHeaders(final int responseCode, final Map headers) { 72 | 73 | final MockResponse response = new MockResponse() 74 | .setResponseCode(responseCode); 75 | for (Map.Entry entry : headers.entrySet()) { 76 | response.addHeader(entry.getKey(), entry.getValue()); 77 | } 78 | 79 | return response; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/InfluxDBClientTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client; 23 | 24 | import java.util.Map; 25 | import java.util.Properties; 26 | 27 | import org.assertj.core.api.Assertions; 28 | import org.junit.jupiter.api.Test; 29 | 30 | import com.influxdb.v3.client.config.ClientConfig; 31 | 32 | public class InfluxDBClientTest { 33 | 34 | @Test 35 | void withProxyUrl() { 36 | String proxyUrl = "http://localhost:10000"; 37 | ClientConfig.Builder builder = new ClientConfig.Builder(); 38 | builder.proxyUrl(proxyUrl); 39 | ClientConfig clientConfig = builder.build(); 40 | Assertions.assertThat(clientConfig.getProxyUrl()).isEqualTo(proxyUrl); 41 | } 42 | 43 | @Test 44 | void withSslRootsFilePath() { 45 | String path = "/path/to/cert"; 46 | ClientConfig.Builder builder = new ClientConfig.Builder(); 47 | builder.sslRootsFilePath(path); 48 | ClientConfig clientConfig = builder.build(); 49 | Assertions.assertThat(clientConfig.sslRootsFilePath()).isEqualTo(path); 50 | } 51 | 52 | @Test 53 | void requiredHost() { 54 | 55 | Assertions.assertThatThrownBy(() -> InfluxDBClient.getInstance(null, "my-token".toCharArray(), "my-database")) 56 | .isInstanceOf(IllegalArgumentException.class) 57 | .hasMessage("The URL of the InfluxDB server has to be defined."); 58 | } 59 | 60 | @Test 61 | void requiredHostConnectionString() { 62 | 63 | Assertions.assertThatThrownBy(() -> InfluxDBClient.getInstance("?token=my-token&database=my-database")) 64 | .isInstanceOf(IllegalArgumentException.class) 65 | .hasMessageContaining("no protocol"); 66 | } 67 | 68 | @Test 69 | void fromParameters() throws Exception { 70 | 71 | try (InfluxDBClient client = InfluxDBClient.getInstance("http://localhost:8086", 72 | "my-token".toCharArray(), "my-database")) { 73 | Assertions.assertThat(client).isNotNull(); 74 | } 75 | } 76 | 77 | @Test 78 | void fromConnectionString() throws Exception { 79 | 80 | try (InfluxDBClient client = InfluxDBClient.getInstance("http://localhost:8086" 81 | + "?token=my-token&database=my-db")) { 82 | Assertions.assertThat(client).isNotNull(); 83 | } 84 | } 85 | 86 | @Test 87 | void fromEnvOrProperties() throws Exception { 88 | 89 | final Properties old = System.getProperties(); 90 | final Properties p = new Properties(); 91 | p.put("influx.host", "http://localhost:8086"); 92 | p.put("influx.token", "my-token"); 93 | p.put("influx.database", "my-db"); 94 | System.setProperties(p); 95 | 96 | try (InfluxDBClient client = InfluxDBClient.getInstance()) { 97 | Assertions.assertThat(client).isNotNull(); 98 | } finally { 99 | System.setProperties(old); 100 | } 101 | } 102 | 103 | @Test 104 | void withDefaultTags() throws Exception { 105 | 106 | Map defaultTags = Map.of("unit", "U2", "model", "M1"); 107 | 108 | try (InfluxDBClient client = InfluxDBClient.getInstance( 109 | "http://localhost:8086", 110 | "MY-TOKEN".toCharArray(), 111 | "MY-DATABASE", 112 | defaultTags)) { 113 | Assertions.assertThat(client).isNotNull(); 114 | } 115 | } 116 | 117 | @Test 118 | public void unsupportedQueryParams() throws Exception { 119 | try (InfluxDBClient client = InfluxDBClient.getInstance("http://localhost:8086", 120 | "my-token".toCharArray(), "my-database")) { 121 | 122 | String query = "select * from cpu where client=$client"; 123 | Map parameters = Map.of("client", client); 124 | 125 | Assertions.assertThatThrownBy(() -> client.queryPoints(query, parameters)) 126 | .isInstanceOf(IllegalArgumentException.class) 127 | .hasMessage("The parameter client value has unsupported type: " 128 | + "class com.influxdb.v3.client.internal.InfluxDBClientImpl"); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/PointTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client; 23 | 24 | import java.math.BigInteger; 25 | import java.time.Instant; 26 | import java.util.HashMap; 27 | import java.util.Map; 28 | 29 | import org.assertj.core.api.Assertions; 30 | import org.junit.jupiter.api.Test; 31 | 32 | import com.influxdb.v3.client.write.WritePrecision; 33 | 34 | public class PointTest { 35 | @Test 36 | void fromValues() throws Exception { 37 | PointValues pointValues = PointValues.measurement("measurement") 38 | .setField("field1", 42); 39 | Point point = Point.fromValues(pointValues); 40 | 41 | Assertions.assertThat("measurement").isEqualTo(point.getMeasurement()); 42 | 43 | Assertions.assertThat(42L).isEqualTo(point.getField("field1")); 44 | point.setMeasurement("newMeasurement"); 45 | Assertions.assertThat("newMeasurement").isEqualTo(point.getMeasurement()); 46 | Assertions.assertThat("newMeasurement").isEqualTo(pointValues.getMeasurement()); 47 | } 48 | 49 | @Test 50 | void setMeasurement() { 51 | Point point = Point.measurement("measurement"); 52 | Assertions.assertThat("measurement").isEqualTo(point.getMeasurement()); 53 | 54 | point.setMeasurement("newMeasurement"); 55 | Assertions.assertThat("newMeasurement").isEqualTo(point.getMeasurement()); 56 | } 57 | 58 | @Test 59 | void setTimestamp() { 60 | Point point = Point.measurement("measurement"); 61 | 62 | Instant timestamp = Instant.parse("2023-11-08T12:00:00Z"); 63 | point.setTimestamp(timestamp); 64 | Assertions.assertThat(BigInteger.valueOf(timestamp.getEpochSecond()) 65 | .multiply(BigInteger.valueOf(1_000_000_000))) 66 | .isEqualTo(point.getTimestamp()); 67 | } 68 | 69 | @Test 70 | void setTags() { 71 | Point point = Point.measurement("measurement"); 72 | 73 | Map tags = new HashMap<>(); 74 | tags.put("tag1", "value1"); 75 | tags.put("tag2", "value2"); 76 | 77 | point.setTags(tags); 78 | 79 | Assertions.assertThat(point.getTag("tag1")).isEqualTo("value1"); 80 | Assertions.assertThat(point.getTag("tag2")).isEqualTo("value2"); 81 | } 82 | 83 | @Test 84 | void removeTag() { 85 | Point point = Point.measurement("measurement") 86 | .setTag("tag1", "value1") 87 | .setTag("tag2", "value2"); 88 | 89 | point.removeTag("tag1"); 90 | point.removeTag("tagNonExistent"); 91 | 92 | Assertions.assertThat(point.getTag("tag1")).isNull(); 93 | Assertions.assertThat(point.getTag("tag2")).isEqualTo("value2"); 94 | } 95 | 96 | @Test 97 | void getTagNames() { 98 | Point point = Point.measurement("measurement") 99 | .setTag("tag1", "value1") 100 | .setTag("tag2", "value2"); 101 | 102 | Assertions.assertThat(point.getTagNames()).isEqualTo(new String[]{"tag1", "tag2"}); 103 | } 104 | 105 | @Test 106 | void setGetTypeField() { 107 | Point point = Point.measurement("measurement"); 108 | 109 | double floatValue = 2.71; 110 | long integerValue = 64L; 111 | boolean booleanValue = true; 112 | String stringValue = "text"; 113 | 114 | point.setFloatField("floatField", floatValue); 115 | point.setIntegerField("integerField", integerValue); 116 | point.setBooleanField("booleanField", booleanValue); 117 | point.setStringField("stringField", stringValue); 118 | 119 | Assertions.assertThat(point.getFloatField("floatField")).isEqualTo(floatValue); 120 | Assertions.assertThat(point.getIntegerField("integerField")).isEqualTo(integerValue); 121 | Assertions.assertThat(point.getBooleanField("booleanField")).isEqualTo(booleanValue); 122 | Assertions.assertThat(point.getStringField("stringField")).isEqualTo(stringValue); 123 | } 124 | 125 | @Test 126 | void fieldGenerics() { 127 | Point point = Point.measurement("measurement"); 128 | 129 | double floatValue = 2.71; 130 | long integerValue = 64L; 131 | boolean booleanValue = true; 132 | String stringValue = "text"; 133 | 134 | point.setField("floatField", floatValue); 135 | point.setField("integerField", integerValue); 136 | point.setField("booleanField", booleanValue); 137 | point.setField("stringField", stringValue); 138 | 139 | Assertions.assertThat(point.getField("floatField", Double.class)).isEqualTo(floatValue); 140 | Assertions.assertThat(point.getFieldType("floatField")).isEqualTo(Double.class); 141 | Assertions.assertThat(point.getField("integerField", Long.class)).isEqualTo(integerValue); 142 | Assertions.assertThat(point.getFieldType("integerField")).isEqualTo(Long.class); 143 | Assertions.assertThat(point.getField("booleanField", Boolean.class)).isEqualTo(booleanValue); 144 | Assertions.assertThat(point.getFieldType("booleanField")).isEqualTo(Boolean.class); 145 | Assertions.assertThat(point.getField("stringField", String.class)).isEqualTo(stringValue); 146 | Assertions.assertThat(point.getFieldType("stringField")).isEqualTo(String.class); 147 | Assertions.assertThat(point.getField("Missing", String.class)).isNull(); 148 | Assertions.assertThat(point.getFieldType("Missing")).isNull(); 149 | } 150 | 151 | @Test 152 | void setFields() { 153 | Point point = Point.measurement("measurement"); 154 | 155 | point.setField("field1", 42); 156 | point.setField("field2", "value"); 157 | point.setField("field3", 3.14); 158 | 159 | Assertions.assertThat(42L).isEqualTo(point.getField("field1")); 160 | Assertions.assertThat("value").isEqualTo(point.getField("field2")); 161 | Assertions.assertThat(3.14).isEqualTo(point.getField("field3")); 162 | } 163 | 164 | @Test 165 | void removeField() { 166 | Point point = Point.measurement("measurement") 167 | .setField("field1", 42) 168 | .setField("field2", "value") 169 | .setField("field3", 3.14); 170 | 171 | point.removeField("field1") 172 | .removeField("field2"); 173 | 174 | Assertions.assertThat(point.getField("field1")).isNull(); 175 | Assertions.assertThat(point.getField("field2")).isNull(); 176 | Assertions.assertThat(3.14).isEqualTo(point.getField("field3")); 177 | } 178 | 179 | @Test 180 | void getFieldNames() { 181 | Point point = Point.measurement("measurement") 182 | .setField("field", 42) 183 | .setField("123", "value") 184 | .setField("some_name", 3.14); 185 | 186 | Assertions.assertThat(point.getFieldNames()) 187 | .isEqualTo(new String[]{"123", "field", "some_name"}); 188 | } 189 | 190 | @Test 191 | void toLineProtocol() { 192 | Point point = Point.measurement("measurement") 193 | .setTag("tag1", "value1") 194 | .setField("field1", 42); 195 | 196 | String lineProtocol = point.toLineProtocol(WritePrecision.NS); 197 | Assertions.assertThat("measurement,tag1=value1 field1=42i").isEqualTo(lineProtocol); 198 | } 199 | 200 | @Test 201 | void copy() { 202 | Point point = Point.measurement("measurement") 203 | .setTag("tag1", "value1") 204 | .setField("field1", 42); 205 | 206 | Point copy = point.copy(); 207 | 208 | // Ensure the copy is not the same object 209 | Assertions.assertThat(point).isNotSameAs(copy); 210 | // Ensure the values are equal 211 | Assertions.assertThat(point.getMeasurement()).isEqualTo(copy.getMeasurement()); 212 | Assertions.assertThat(point.getTag("tag1")).isEqualTo(copy.getTag("tag1")); 213 | Assertions.assertThat(point.getField("field1")).isEqualTo(copy.getField("field1")); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/PointValuesTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client; 23 | 24 | import java.math.BigInteger; 25 | import java.time.Instant; 26 | import java.util.HashMap; 27 | import java.util.Map; 28 | 29 | import org.assertj.core.api.Assertions; 30 | import org.junit.jupiter.api.Test; 31 | 32 | public class PointValuesTest { 33 | @Test 34 | void asPoint() { 35 | PointValues pointValues = new PointValues() 36 | .setTag("tag1", "value1") 37 | .setField("field1", 42); 38 | 39 | Point point = pointValues.asPoint("measurement"); 40 | 41 | Assertions.assertThat(pointValues.getMeasurement()).isEqualTo(point.getMeasurement()); 42 | Assertions.assertThat(pointValues.getTag("tag1")).isEqualTo(point.getTag("tag1")); 43 | Assertions.assertThat(pointValues.getField("field1")).isEqualTo(point.getField("field1")); 44 | } 45 | 46 | @Test 47 | void setMeasurement() { 48 | PointValues pointValues = PointValues.measurement("measurement"); 49 | Assertions.assertThat("measurement").isEqualTo(pointValues.getMeasurement()); 50 | 51 | pointValues.setMeasurement("newMeasurement"); 52 | Assertions.assertThat("newMeasurement").isEqualTo(pointValues.getMeasurement()); 53 | } 54 | 55 | @Test 56 | void setTimestamp() { 57 | PointValues pointValues = PointValues.measurement("measurement"); 58 | 59 | Instant timestamp = Instant.parse("2023-11-08T12:00:00Z"); 60 | pointValues.setTimestamp(timestamp); 61 | Assertions.assertThat(BigInteger.valueOf(timestamp.getEpochSecond()) 62 | .multiply(BigInteger.valueOf(1_000_000_000))) 63 | .isEqualTo(pointValues.getTimestamp()); 64 | } 65 | 66 | @Test 67 | void setTags() { 68 | PointValues pointValues = PointValues.measurement("measurement"); 69 | 70 | Map tags = new HashMap<>(); 71 | tags.put("tag1", "value1"); 72 | tags.put("tag2", "value2"); 73 | 74 | pointValues.setTags(tags); 75 | 76 | Assertions.assertThat("value1").isEqualTo(pointValues.getTag("tag1")); 77 | Assertions.assertThat("value2").isEqualTo(pointValues.getTag("tag2")); 78 | } 79 | 80 | @Test 81 | void removeTag() { 82 | PointValues pointValues = PointValues.measurement("measurement") 83 | .setTag("tag1", "value1") 84 | .setTag("tag2", "value2"); 85 | 86 | pointValues.removeTag("tag1"); 87 | pointValues.removeTag("tagNonExistent"); 88 | 89 | Assertions.assertThat(pointValues.getTag("tag1")).isNull(); 90 | Assertions.assertThat(pointValues.getTag("tag2")).isEqualTo("value2"); 91 | } 92 | 93 | @Test 94 | void getTagNames() { 95 | PointValues pointValues = PointValues.measurement("measurement") 96 | .setTag("tag1", "value1") 97 | .setTag("tag2", "value2"); 98 | 99 | Assertions.assertThat(pointValues.getTagNames()).isEqualTo(new String[]{"tag1", "tag2"}); 100 | } 101 | 102 | @Test 103 | void setGetTypeField() { 104 | PointValues pointValues = PointValues.measurement("measurement"); 105 | 106 | double floatValue = 2.71; 107 | long integerValue = 64L; 108 | boolean booleanValue = true; 109 | String stringValue = "text"; 110 | 111 | pointValues.setFloatField("floatField", floatValue); 112 | pointValues.setIntegerField("integerField", integerValue); 113 | pointValues.setBooleanField("booleanField", booleanValue); 114 | pointValues.setStringField("stringField", stringValue); 115 | 116 | Assertions.assertThat(pointValues.getFloatField("floatField")).isEqualTo(floatValue); 117 | Assertions.assertThat(pointValues.getIntegerField("integerField")).isEqualTo(integerValue); 118 | Assertions.assertThat(pointValues.getBooleanField("booleanField")).isEqualTo(booleanValue); 119 | Assertions.assertThat(pointValues.getStringField("stringField")).isEqualTo(stringValue); 120 | } 121 | 122 | @Test 123 | void fieldGenerics() { 124 | PointValues pointValues = PointValues.measurement("measurement"); 125 | 126 | double floatValue = 2.71; 127 | long integerValue = 64L; 128 | boolean booleanValue = true; 129 | String stringValue = "text"; 130 | 131 | pointValues.setField("floatField", floatValue); 132 | pointValues.setField("integerField", integerValue); 133 | pointValues.setField("booleanField", booleanValue); 134 | pointValues.setField("stringField", stringValue); 135 | 136 | Assertions.assertThat(pointValues.getField("floatField", Double.class)).isEqualTo(floatValue); 137 | Assertions.assertThat(pointValues.getFieldType("floatField")).isEqualTo(Double.class); 138 | Assertions.assertThat(pointValues.getField("integerField", Long.class)).isEqualTo(integerValue); 139 | Assertions.assertThat(pointValues.getFieldType("integerField")).isEqualTo(Long.class); 140 | Assertions.assertThat(pointValues.getField("booleanField", Boolean.class)).isEqualTo(booleanValue); 141 | Assertions.assertThat(pointValues.getFieldType("booleanField")).isEqualTo(Boolean.class); 142 | Assertions.assertThat(pointValues.getField("stringField", String.class)).isEqualTo(stringValue); 143 | Assertions.assertThat(pointValues.getFieldType("stringField")).isEqualTo(String.class); 144 | Assertions.assertThat(pointValues.getField("Missing", String.class)).isNull(); 145 | Assertions.assertThat(pointValues.getFieldType("Missing")).isNull(); 146 | } 147 | 148 | @Test 149 | void setFields() { 150 | PointValues pointValues = PointValues.measurement("measurement"); 151 | 152 | pointValues.setField("field1", 42); 153 | pointValues.setField("field2", "value"); 154 | pointValues.setField("field3", 3.14); 155 | 156 | Assertions.assertThat(42L).isEqualTo(pointValues.getField("field1")); 157 | Assertions.assertThat("value").isEqualTo(pointValues.getField("field2")); 158 | Assertions.assertThat(3.14).isEqualTo(pointValues.getField("field3")); 159 | } 160 | 161 | @Test 162 | void removeField() { 163 | PointValues pointValues = PointValues.measurement("measurement") 164 | .setField("field1", 42) 165 | .setField("field2", "value") 166 | .setField("field3", 3.14); 167 | 168 | pointValues.removeField("field1") 169 | .removeField("field2"); 170 | 171 | Assertions.assertThat(pointValues.getField("field1")).isNull(); 172 | Assertions.assertThat(pointValues.getField("field2")).isNull(); 173 | Assertions.assertThat(3.14).isEqualTo(pointValues.getField("field3")); 174 | } 175 | 176 | @Test 177 | void getFieldNames() { 178 | PointValues pointValues = PointValues.measurement("measurement") 179 | .setField("field", 42) 180 | .setField("123", "value") 181 | .setField("some_name", 3.14); 182 | 183 | Assertions.assertThat(pointValues.getFieldNames()) 184 | .isEqualTo(new String[]{"123", "field", "some_name"}); 185 | } 186 | 187 | @Test 188 | void copy() { 189 | PointValues pointValues = PointValues.measurement("measurement") 190 | .setTag("tag1", "value1") 191 | .setField("field1", 42); 192 | 193 | PointValues copy = pointValues.copy(); 194 | 195 | // Ensure the copy is not the same object 196 | Assertions.assertThat(pointValues).isNotSameAs(copy); 197 | // Ensure the values are equal 198 | Assertions.assertThat(pointValues.getMeasurement()).isEqualTo(copy.getMeasurement()); 199 | Assertions.assertThat(pointValues.getTag("tag1")).isEqualTo(copy.getTag("tag1")); 200 | Assertions.assertThat(pointValues.getField("field1")).isEqualTo(copy.getField("field1")); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/internal/ArgumentsDurationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import org.assertj.core.api.Assertions; 25 | import org.junit.jupiter.api.Test; 26 | 27 | /** 28 | * @author Jakub Bednar (bednar@github) (20/08/2018 12:31) 29 | */ 30 | @SuppressWarnings("ConstantConditions") 31 | class ArgumentsDurationTest { 32 | 33 | @Test 34 | void literals() { 35 | 36 | Arguments.checkDuration("1s", "duration"); 37 | Arguments.checkDuration("10d", "duration"); 38 | Arguments.checkDuration("1h15m", "duration"); 39 | Arguments.checkDuration("5w", "duration"); 40 | Arguments.checkDuration("1mo5d", "duration"); 41 | Arguments.checkDuration("-1mo5d", "duration"); 42 | Arguments.checkDuration("inf", "duration"); 43 | Arguments.checkDuration("-inf", "duration"); 44 | } 45 | 46 | @Test 47 | void literalNull() { 48 | 49 | Assertions.assertThatThrownBy(() -> Arguments.checkDuration(null, "duration")) 50 | .isInstanceOf(IllegalArgumentException.class) 51 | .hasMessage("Expecting a duration string for duration. But got: null"); 52 | } 53 | 54 | @Test 55 | void literalEmpty() { 56 | 57 | Assertions.assertThatThrownBy(() -> Arguments.checkDuration("", "duration")) 58 | .isInstanceOf(IllegalArgumentException.class) 59 | .hasMessage("Expecting a duration string for duration. But got: "); 60 | } 61 | 62 | @Test 63 | void literalNotDuration() { 64 | 65 | Assertions.assertThatThrownBy(() -> Arguments.checkDuration("x", "duration")) 66 | .isInstanceOf(IllegalArgumentException.class) 67 | .hasMessage("Expecting a duration string for duration. But got: x"); 68 | } 69 | 70 | @Test 71 | void notRequiredValid() { 72 | 73 | Arguments.checkDurationNotRequired(null, "duration"); 74 | Arguments.checkDurationNotRequired("", "duration"); 75 | Arguments.checkDurationNotRequired("1s", "duration"); 76 | } 77 | 78 | @Test 79 | void notRequiredNotValid() { 80 | 81 | Assertions.assertThatThrownBy(() -> Arguments.checkDurationNotRequired("x", "duration")) 82 | .isInstanceOf(IllegalArgumentException.class) 83 | .hasMessage("Expecting a duration string for duration. But got: x"); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/internal/ArgumentsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import java.time.temporal.ChronoUnit; 25 | 26 | import org.assertj.core.api.Assertions; 27 | import org.junit.jupiter.api.Test; 28 | 29 | /** 30 | * @author Jakub Bednar (bednar@github) (01/08/2018 15:29) 31 | */ 32 | @SuppressWarnings("ConstantConditions") 33 | class ArgumentsTest { 34 | 35 | @Test 36 | void checkNonEmptyString() { 37 | 38 | Arguments.checkNonEmpty("valid", "property"); 39 | } 40 | 41 | @Test 42 | void checkNonEmptyStringEmpty() { 43 | 44 | Assertions.assertThatThrownBy(() -> Arguments.checkNonEmpty("", "property")) 45 | .isInstanceOf(IllegalArgumentException.class) 46 | .hasMessage("Expecting a non-empty string for property"); 47 | } 48 | 49 | @Test 50 | void checkNonEmptyStringNull() { 51 | 52 | Assertions.assertThatThrownBy(() -> Arguments.checkNonEmpty(null, "property")) 53 | .isInstanceOf(IllegalArgumentException.class) 54 | .hasMessage("Expecting a non-empty string for property"); 55 | } 56 | 57 | @Test 58 | void checkPositiveNumber() { 59 | 60 | Arguments.checkPositiveNumber(10, "property"); 61 | } 62 | 63 | @Test 64 | void checkPositiveNumberNull() { 65 | 66 | Assertions.assertThatThrownBy(() -> Arguments.checkPositiveNumber(null, "property")) 67 | .isInstanceOf(IllegalArgumentException.class) 68 | .hasMessage("Expecting a positive number for property"); 69 | } 70 | 71 | @Test 72 | void checkPositiveNumberZero() { 73 | 74 | Assertions.assertThatThrownBy(() -> Arguments.checkPositiveNumber(0, "property")) 75 | .isInstanceOf(IllegalArgumentException.class) 76 | .hasMessage("Expecting a positive number for property"); 77 | } 78 | 79 | @Test 80 | void checkPositiveNumberZeroNegative() { 81 | 82 | Assertions.assertThatThrownBy(() -> Arguments.checkPositiveNumber(-12L, "property")) 83 | .isInstanceOf(IllegalArgumentException.class) 84 | .hasMessage("Expecting a positive number for property"); 85 | } 86 | 87 | @Test 88 | void checkNotNegativeNumber() { 89 | 90 | Arguments.checkNotNegativeNumber(0, "valid"); 91 | } 92 | 93 | @Test 94 | void checkNotNegativeNumberNull() { 95 | 96 | Assertions.assertThatThrownBy(() -> Arguments.checkNotNegativeNumber(null, "property")) 97 | .isInstanceOf(IllegalArgumentException.class) 98 | .hasMessage("Expecting a positive or zero number for property"); 99 | } 100 | 101 | @Test 102 | void checkNotNegativeNumberNegative() { 103 | 104 | Assertions.assertThatThrownBy(() -> Arguments.checkNotNegativeNumber(-12L, "property")) 105 | .isInstanceOf(IllegalArgumentException.class) 106 | .hasMessage("Expecting a positive or zero number for property"); 107 | } 108 | 109 | @Test 110 | void checkOneCharString() { 111 | 112 | Arguments.checkOneCharString("#", "valid"); 113 | } 114 | 115 | @Test 116 | void checkOneCharStringEmpty() { 117 | 118 | Assertions.assertThatThrownBy(() -> Arguments.checkOneCharString("", "property")) 119 | .isInstanceOf(IllegalArgumentException.class) 120 | .hasMessage("Expecting a one char string for property"); 121 | } 122 | 123 | @Test 124 | void checkOneCharStringNull() { 125 | 126 | Assertions.assertThatThrownBy(() -> Arguments.checkOneCharString(null, "property")) 127 | .isInstanceOf(IllegalArgumentException.class) 128 | .hasMessage("Expecting a one char string for property"); 129 | } 130 | 131 | @Test 132 | void checkOneCharStringLarge() { 133 | 134 | Assertions.assertThatThrownBy(() -> Arguments.checkOneCharString("##", "property")) 135 | .isInstanceOf(IllegalArgumentException.class) 136 | .hasMessage("Expecting a one char string for property"); 137 | } 138 | 139 | @Test 140 | void checkNotNull() { 141 | Arguments.checkNotNull("value", "property"); 142 | } 143 | 144 | @Test 145 | void checkNotNullFail() { 146 | Assertions.assertThatThrownBy(() -> Arguments.checkNotNull(null, "property")) 147 | .isInstanceOf(NullPointerException.class) 148 | .hasMessage("Expecting a not null reference for property"); 149 | } 150 | 151 | @Test 152 | void checkPrecision() { 153 | 154 | Arguments.checkPrecision(ChronoUnit.SECONDS); 155 | } 156 | 157 | @Test 158 | void checkPrecisionNotSupported() { 159 | 160 | Assertions.assertThatThrownBy(() -> Arguments.checkPrecision(ChronoUnit.DAYS)) 161 | .isInstanceOf(IllegalArgumentException.class) 162 | .hasMessage("Precision must be one of: [Nanos, Micros, Millis, Seconds]"); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/internal/GrpcCallOptionsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import io.grpc.stub.AbstractStub; 25 | import org.apache.arrow.flight.CallOption; 26 | import org.apache.arrow.flight.CallOptions; 27 | import org.junit.jupiter.api.Test; 28 | 29 | import static org.junit.jupiter.api.Assertions.assertEquals; 30 | import static org.junit.jupiter.api.Assertions.assertNotEquals; 31 | import static org.junit.jupiter.api.Assertions.assertNotNull; 32 | 33 | class GrpcCallOptionsTest { 34 | 35 | @Test 36 | void testNotSetMaxInboundMessageSize() { 37 | GrpcCallOptions grpcCallOptions = new GrpcCallOptions.Builder().build(); 38 | assertNotNull(grpcCallOptions); 39 | assertEquals(Integer.MAX_VALUE, grpcCallOptions.getMaxInboundMessageSize()); 40 | } 41 | 42 | @Test 43 | void testSetMaxInboundMessageSize() { 44 | GrpcCallOptions grpcCallOptions = new GrpcCallOptions.Builder() 45 | .withMaxInboundMessageSize(2000) 46 | .build(); 47 | assertNotNull(grpcCallOptions); 48 | assertEquals(2000, grpcCallOptions.getMaxInboundMessageSize()); 49 | } 50 | 51 | @Test 52 | void testMergeCallOptionsWithBothNonNullArrays() { 53 | CallOption option1 = callOption(); 54 | CallOption option2 = callOption(); 55 | CallOption[] baseCallOptions = {option1}; 56 | CallOption[] additionalCallOptions = {option2}; 57 | 58 | CallOption[] result = GrpcCallOptions.mergeCallOptions(baseCallOptions, additionalCallOptions); 59 | 60 | assertNotNull(result); 61 | assertEquals(2, result.length); 62 | assertEquals(option1, result[0]); 63 | assertEquals(option2, result[1]); 64 | } 65 | 66 | @Test 67 | void testMergeCallOptionsWithBaseCallOptionsNull() { 68 | CallOption option1 = callOption(); 69 | CallOption option2 = callOption(); 70 | CallOption[] baseCallOptions = null; 71 | CallOption[] additionalCallOptions = {option1, option2}; 72 | 73 | CallOption[] result = GrpcCallOptions.mergeCallOptions(baseCallOptions, additionalCallOptions); 74 | 75 | assertNotNull(result); 76 | assertEquals(2, result.length); 77 | assertEquals(option1, result[0]); 78 | assertEquals(option2, result[1]); 79 | } 80 | 81 | @Test 82 | void testMergeCallOptionsWithAdditionalCallOptionsNull() { 83 | CallOption option1 = callOption(); 84 | CallOption[] baseCallOptions = {option1}; 85 | CallOption[] additionalCallOptions = null; 86 | 87 | CallOption[] result = GrpcCallOptions.mergeCallOptions(baseCallOptions, additionalCallOptions); 88 | 89 | assertNotNull(result); 90 | assertEquals(1, result.length); 91 | assertEquals(option1, result[0]); 92 | } 93 | 94 | @Test 95 | void testMergeCallOptionsWithBothArraysNull() { 96 | CallOption[] result = GrpcCallOptions.mergeCallOptions(null, null); 97 | 98 | assertNotNull(result); 99 | assertEquals(0, result.length); 100 | } 101 | 102 | @Test 103 | void testMergeCallOptionsWithEmptyArrays() { 104 | CallOption[] baseCallOptions = {}; 105 | CallOption[] additionalCallOptions = {}; 106 | 107 | CallOption[] result = GrpcCallOptions.mergeCallOptions(baseCallOptions, additionalCallOptions); 108 | 109 | assertNotNull(result); 110 | assertEquals(0, result.length); 111 | } 112 | 113 | private CallOption callOption() { 114 | return new CallOptions.GrpcCallOption() { 115 | @Override 116 | public > T wrapStub(final T stub) { 117 | return stub.withMaxInboundMessageSize(Integer.MAX_VALUE); 118 | } 119 | }; 120 | } 121 | 122 | @Test 123 | void testEqualsWithEqualObjects() { 124 | GrpcCallOptions options1 = new GrpcCallOptions.Builder() 125 | .withMaxInboundMessageSize(2000) 126 | .withCompressorName("gzip") 127 | .build(); 128 | GrpcCallOptions options2 = new GrpcCallOptions.Builder() 129 | .withMaxInboundMessageSize(2000) 130 | .withCompressorName("gzip") 131 | .build(); 132 | 133 | assertEquals(options1, options2); 134 | } 135 | 136 | @Test 137 | void testEqualsWithDifferentObjects() { 138 | GrpcCallOptions options1 = new GrpcCallOptions.Builder() 139 | .withMaxInboundMessageSize(2000) 140 | .withCompressorName("gzip") 141 | .build(); 142 | GrpcCallOptions options2 = new GrpcCallOptions.Builder() 143 | .withMaxInboundMessageSize(1000) 144 | .withCompressorName("deflate") 145 | .build(); 146 | 147 | assertNotEquals(options1, options2); 148 | } 149 | 150 | @Test 151 | void testEqualsWithNullAndDifferentClass() { 152 | GrpcCallOptions options = new GrpcCallOptions.Builder() 153 | .withMaxInboundMessageSize(2000) 154 | .build(); 155 | 156 | assertNotEquals(null, options); 157 | assertNotEquals(1, options); 158 | } 159 | 160 | @Test 161 | void testHashCodeWithEqualObjects() { 162 | GrpcCallOptions options1 = new GrpcCallOptions.Builder() 163 | .withMaxInboundMessageSize(2000) 164 | .withCompressorName("gzip") 165 | .build(); 166 | GrpcCallOptions options2 = new GrpcCallOptions.Builder() 167 | .withMaxInboundMessageSize(2000) 168 | .withCompressorName("gzip") 169 | .build(); 170 | 171 | assertEquals(options1.hashCode(), options2.hashCode()); 172 | } 173 | 174 | @Test 175 | void testHashCodeWithDifferentObjects() { 176 | GrpcCallOptions options1 = new GrpcCallOptions.Builder() 177 | .withMaxInboundMessageSize(2000) 178 | .withCompressorName("gzip") 179 | .build(); 180 | GrpcCallOptions options2 = new GrpcCallOptions.Builder() 181 | .withMaxInboundMessageSize(1000) 182 | .withCompressorName("deflate") 183 | .build(); 184 | 185 | assertNotEquals(options1.hashCode(), options2.hashCode()); 186 | } 187 | 188 | @Test 189 | void testToString() { 190 | GrpcCallOptions options = new GrpcCallOptions.Builder() 191 | .withMaxInboundMessageSize(2000) 192 | .withMaxOutboundMessageSize(5000) 193 | .withCompressorName("gzip") 194 | .build(); 195 | 196 | String expected = "GrpcCallOptions{deadline=null, " 197 | + "executor=null, " 198 | + "compressorName='gzip', " 199 | + "waitForReady=null, " 200 | + "maxInboundMessageSize=2000, " 201 | + "maxOutboundMessageSize=5000}"; 202 | assertEquals(expected, options.toString()); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/internal/NanosecondConverterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import java.math.BigInteger; 25 | 26 | import org.apache.arrow.vector.types.TimeUnit; 27 | import org.apache.arrow.vector.types.pojo.ArrowType; 28 | import org.apache.arrow.vector.types.pojo.Field; 29 | import org.apache.arrow.vector.types.pojo.FieldType; 30 | import org.junit.jupiter.api.Assertions; 31 | import org.junit.jupiter.api.Test; 32 | 33 | public class NanosecondConverterTest { 34 | 35 | @Test 36 | void testGetTimestampNanosecond() { 37 | BigInteger timestampNanoSecond = null; 38 | 39 | // Second 40 | FieldType timeTypeSecond = new FieldType(true, 41 | new ArrowType.Timestamp(TimeUnit.SECOND, "UTC"), 42 | null); 43 | Field timeFieldSecond = new Field("time", timeTypeSecond, null); 44 | timestampNanoSecond = NanosecondConverter.getTimestampNano(123_456L, timeFieldSecond); 45 | Assertions.assertEquals( 46 | BigInteger.valueOf(123_456L) 47 | .multiply(BigInteger.valueOf(1_000_000_000)), timestampNanoSecond 48 | ); 49 | 50 | // MilliSecond 51 | FieldType timeTypeMilliSecond = new FieldType(true, 52 | new ArrowType.Timestamp(TimeUnit.MILLISECOND, "UTC"), 53 | null); 54 | Field timeFieldMilliSecond = new Field("time", timeTypeMilliSecond, null); 55 | timestampNanoSecond = NanosecondConverter.getTimestampNano(123_456L, timeFieldMilliSecond); 56 | Assertions.assertEquals( 57 | BigInteger.valueOf(123_456L) 58 | .multiply(BigInteger.valueOf(1_000_000)), timestampNanoSecond 59 | ); 60 | 61 | // MicroSecond 62 | FieldType timeTypeMicroSecond = new FieldType(true, 63 | new ArrowType.Timestamp(TimeUnit.MICROSECOND, "UTC"), 64 | null); 65 | Field timeFieldMicroSecond = new Field("time", timeTypeMicroSecond, null); 66 | timestampNanoSecond = NanosecondConverter.getTimestampNano(123_456L, timeFieldMicroSecond); 67 | Assertions.assertEquals( 68 | BigInteger.valueOf(123_456L) 69 | .multiply(BigInteger.valueOf(1_000)), timestampNanoSecond 70 | ); 71 | 72 | // Nano Second 73 | FieldType timeTypeNanoSecond = new FieldType(true, 74 | new ArrowType.Timestamp(TimeUnit.NANOSECOND, "UTC"), 75 | null); 76 | Field timeFieldNanoSecond = new Field("time", timeTypeNanoSecond, null); 77 | timestampNanoSecond = NanosecondConverter.getTimestampNano(123_456L, timeFieldNanoSecond); 78 | Assertions.assertEquals(BigInteger.valueOf(123_456L), timestampNanoSecond); 79 | 80 | // For ArrowType.Time type 81 | FieldType timeMilliSecond = new FieldType(true, 82 | new ArrowType.Time(TimeUnit.MILLISECOND, 32), 83 | null); 84 | Field fieldMilliSecond = new Field("time", timeMilliSecond, null); 85 | timestampNanoSecond = NanosecondConverter.getTimestampNano(123_456L, fieldMilliSecond); 86 | Assertions.assertEquals( 87 | BigInteger.valueOf(123_456L) 88 | .multiply(BigInteger.valueOf(1_000_000)), timestampNanoSecond 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/internal/TypeCastingTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import org.apache.arrow.vector.util.Text; 25 | import org.junit.jupiter.api.Assertions; 26 | import org.junit.jupiter.api.Test; 27 | 28 | public class TypeCastingTest { 29 | 30 | @Test 31 | void testToLongValue() { 32 | Assertions.assertEquals(1, TypeCasting.toLongValue(1)); 33 | Assertions.assertEquals(1.0, TypeCasting.toLongValue(1.23)); 34 | 35 | Assertions.assertThrows(ClassCastException.class, 36 | () -> TypeCasting.toLongValue("1")); 37 | } 38 | 39 | @Test 40 | void testToDoubleValue() { 41 | Assertions.assertEquals(1.23, TypeCasting.toDoubleValue(1.23)); 42 | Assertions.assertEquals(1.0, TypeCasting.toDoubleValue(1)); 43 | 44 | Assertions.assertThrows(ClassCastException.class, 45 | () -> TypeCasting.toDoubleValue("1.2")); 46 | } 47 | 48 | @Test 49 | void testToStringValue() { 50 | Assertions.assertEquals("test", TypeCasting.toStringValue("test")); 51 | Assertions.assertEquals("test", 52 | TypeCasting.toStringValue(new Text("test"))); 53 | 54 | Assertions.assertThrows(ClassCastException.class, 55 | () -> TypeCasting.toStringValue(1)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/internal/VectorSchemaRootUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import java.util.Arrays; 25 | import java.util.HashMap; 26 | import java.util.List; 27 | import java.util.Map; 28 | import javax.annotation.Nonnull; 29 | 30 | import org.apache.arrow.memory.RootAllocator; 31 | import org.apache.arrow.vector.BigIntVector; 32 | import org.apache.arrow.vector.BitVector; 33 | import org.apache.arrow.vector.Float8Vector; 34 | import org.apache.arrow.vector.TimeMilliVector; 35 | import org.apache.arrow.vector.VarCharVector; 36 | import org.apache.arrow.vector.VectorSchemaRoot; 37 | import org.apache.arrow.vector.types.FloatingPointPrecision; 38 | import org.apache.arrow.vector.types.TimeUnit; 39 | import org.apache.arrow.vector.types.pojo.ArrowType; 40 | import org.apache.arrow.vector.types.pojo.Field; 41 | import org.apache.arrow.vector.types.pojo.FieldType; 42 | import org.apache.arrow.vector.types.pojo.Schema; 43 | 44 | public final class VectorSchemaRootUtils { 45 | 46 | private VectorSchemaRootUtils() { } 47 | 48 | public static VectorSchemaRoot generateInvalidVectorSchemaRoot() { 49 | Field testField = generateInvalidIntField("test_field"); 50 | Field testField1 = generateInvalidUnsignedIntField("test_field1"); 51 | Field testField2 = generateInvalidFloatField("test_field2"); 52 | Field testField3 = generateInvalidStringField("test_field3"); 53 | Field testField4 = generateInvalidBoolField("test_field4"); 54 | 55 | List fields = List.of(testField, 56 | testField1, 57 | testField2, 58 | testField3, 59 | testField4); 60 | 61 | VectorSchemaRoot root = initializeVectorSchemaRoot(fields.toArray(new Field[0])); 62 | 63 | VarCharVector intVector = (VarCharVector) root.getVector("test_field"); 64 | intVector.allocateNew(); 65 | intVector.set(0, "aaaa".getBytes()); 66 | 67 | VarCharVector uIntVector = (VarCharVector) root.getVector("test_field1"); 68 | uIntVector.allocateNew(); 69 | uIntVector.set(0, "aaaa".getBytes()); 70 | 71 | VarCharVector floatVector = (VarCharVector) root.getVector("test_field2"); 72 | floatVector.allocateNew(); 73 | floatVector.set(0, "aaaa".getBytes()); 74 | 75 | Float8Vector stringVector = (Float8Vector) root.getVector("test_field3"); 76 | stringVector.allocateNew(); 77 | stringVector.set(0, 100.2); 78 | 79 | VarCharVector booleanVector = (VarCharVector) root.getVector("test_field4"); 80 | booleanVector.allocateNew(); 81 | booleanVector.set(0, "aaa".getBytes()); 82 | 83 | return root; 84 | } 85 | 86 | public static VectorSchemaRoot generateVectorSchemaRoot() { 87 | Field measurementField = generateStringField("measurement"); 88 | Field timeField = generateTimeField(); 89 | Field memTotalField = generateIntField("mem_total"); 90 | Field diskFreeField = generateUnsignedIntField("disk_free"); 91 | Field temperatureField = generateFloatField("temperature"); 92 | Field nameField = generateStringField("name"); 93 | Field isActiveField = generateBoolField("isActive"); 94 | List fields = List.of(measurementField, 95 | timeField, 96 | memTotalField, 97 | diskFreeField, 98 | temperatureField, 99 | nameField, 100 | isActiveField); 101 | 102 | VectorSchemaRoot root = initializeVectorSchemaRoot(fields.toArray(new Field[0])); 103 | VarCharVector measurement = (VarCharVector) root.getVector("measurement"); 104 | measurement.allocateNew(); 105 | measurement.set(0, "host".getBytes()); 106 | 107 | TimeMilliVector timeVector = (TimeMilliVector) root.getVector("time"); 108 | timeVector.allocateNew(); 109 | timeVector.setSafe(0, 123_456); 110 | 111 | BigIntVector intVector = (BigIntVector) root.getVector("mem_total"); 112 | intVector.allocateNew(); 113 | intVector.set(0, 2048); 114 | 115 | BigIntVector unsignedIntVector = (BigIntVector) root.getVector("disk_free"); 116 | unsignedIntVector.allocateNew(); 117 | unsignedIntVector.set(0, 1_000_000); 118 | 119 | Float8Vector floatVector = (Float8Vector) root.getVector("temperature"); 120 | floatVector.allocateNew(); 121 | floatVector.set(0, 100.8766); 122 | 123 | VarCharVector stringVector = (VarCharVector) root.getVector("name"); 124 | stringVector.allocateNew(); 125 | stringVector.setSafe(0, "intel".getBytes()); 126 | 127 | BitVector boolVector = (BitVector) root.getVector("isActive"); 128 | boolVector.allocateNew(); 129 | boolVector.setSafe(0, 1); 130 | 131 | return root; 132 | } 133 | 134 | @Nonnull 135 | public static VectorSchemaRoot initializeVectorSchemaRoot(@Nonnull final Field... fields) { 136 | 137 | Schema schema = new Schema(Arrays.asList(fields)); 138 | 139 | VectorSchemaRoot root = VectorSchemaRoot.create(schema, new RootAllocator(Long.MAX_VALUE)); 140 | root.allocateNew(); 141 | 142 | return root; 143 | } 144 | 145 | public static Field generateIntField(final String fieldName) { 146 | Map metadata = new HashMap<>(); 147 | metadata.put("iox::column::type", "iox::column_type::field::integer"); 148 | FieldType intType = new FieldType(true, 149 | new ArrowType.Int(64, true), 150 | null, 151 | metadata); 152 | return new Field(fieldName, intType, null); 153 | } 154 | 155 | public static Field generateInvalidIntField(final String fieldName) { 156 | Map metadata = new HashMap<>(); 157 | metadata.put("iox::column::type", "iox::column_type::field::integer"); 158 | FieldType intType = new FieldType(true, 159 | new ArrowType.Utf8(), 160 | null, 161 | metadata); 162 | return new Field(fieldName, intType, null); 163 | } 164 | 165 | public static Field generateUnsignedIntField(final String fieldName) { 166 | Map metadata = new HashMap<>(); 167 | metadata.put("iox::column::type", "iox::column_type::field::uinteger"); 168 | FieldType intType = new FieldType(true, 169 | new ArrowType.Int(64, true), 170 | null, 171 | metadata); 172 | return new Field(fieldName, intType, null); 173 | } 174 | 175 | public static Field generateInvalidUnsignedIntField(final String fieldName) { 176 | Map metadata = new HashMap<>(); 177 | metadata.put("iox::column::type", "iox::column_type::field::uinteger"); 178 | FieldType intType = new FieldType(true, 179 | new ArrowType.Utf8(), 180 | null, 181 | metadata); 182 | return new Field(fieldName, intType, null); 183 | } 184 | 185 | public static Field generateFloatField(final String fieldName) { 186 | Map metadata = new HashMap<>(); 187 | metadata.put("iox::column::type", "iox::column_type::field::float"); 188 | FieldType floatType = new FieldType(true, 189 | new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE), 190 | null, 191 | metadata); 192 | return new Field(fieldName, floatType, null); 193 | } 194 | 195 | public static Field generateInvalidFloatField(final String fieldName) { 196 | Map metadata = new HashMap<>(); 197 | metadata.put("iox::column::type", "iox::column_type::field::float"); 198 | FieldType floatType = new FieldType(true, 199 | new ArrowType.Utf8(), 200 | null, 201 | metadata); 202 | return new Field(fieldName, floatType, null); 203 | } 204 | 205 | public static Field generateStringField(final String fieldName) { 206 | Map metadata = new HashMap<>(); 207 | metadata.put("iox::column::type", "iox::column_type::field::string"); 208 | FieldType stringType = new FieldType(true, new ArrowType.Utf8(), null, metadata); 209 | return new Field(fieldName, stringType, null); 210 | } 211 | 212 | public static Field generateInvalidStringField(final String fieldName) { 213 | Map metadata = new HashMap<>(); 214 | metadata.put("iox::column::type", "iox::column_type::field::string"); 215 | FieldType stringType = new FieldType(true, 216 | new ArrowType.FloatingPoint( 217 | FloatingPointPrecision.DOUBLE), 218 | null, 219 | metadata); 220 | return new Field(fieldName, stringType, null); 221 | } 222 | 223 | public static Field generateBoolField(final String fieldName) { 224 | Map metadata = new HashMap<>(); 225 | metadata.put("iox::column::type", "iox::column_type::field::boolean"); 226 | FieldType boolType = new FieldType(true, new ArrowType.Bool(), null, metadata); 227 | return new Field(fieldName, boolType, null); 228 | } 229 | 230 | public static Field generateInvalidBoolField(final String fieldName) { 231 | Map metadata = new HashMap<>(); 232 | metadata.put("iox::column::type", "iox::column_type::field::boolean"); 233 | FieldType boolType = new FieldType(true, new ArrowType.Utf8(), null, metadata); 234 | return new Field(fieldName, boolType, null); 235 | } 236 | 237 | public static Field generateTimeField() { 238 | FieldType timeType = new FieldType(true, 239 | new ArrowType.Time(TimeUnit.MILLISECOND, 32), 240 | null); 241 | return new Field("time", timeType, null); 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/testdata/docker.com.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFxDCCBKygAwIBAgIQDE0Qon/WHglZ2jXwZzD0xjANBgkqhkiG9w0BAQsFADA8 3 | MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRwwGgYDVQQDExNBbWF6b24g 4 | UlNBIDIwNDggTTAyMB4XDTI0MDgxODAwMDAwMFoXDTI1MDkxNjIzNTk1OVowFzEV 5 | MBMGA1UEAwwMKi5kb2NrZXIuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 6 | CgKCAQEAytONEwLLBXfyepD/iE7N76+xGDWE/7g21rpPRejCobpaKGVKSL4Y8Pf9 7 | whjh5pzBLxcpQMpYcg/oW+Cp4scjhXyi9yqrOC2Vf26DRA3ufjbescZUjPP28mPO 8 | N1gGQhnr0Sa7mbhNo5JVE7yxLrjhAZFCdEpl1LYdfosYxeBowVOaxBsGfUWCxYuI 9 | HbolTUUNWJKaAN7knKmrHF0a2a6BftaTyFK/6N1FV3rXs5oD+5DEYVFN8193fdz5 10 | DUcf5p7xzjx9yXmHfdomznUPL5Sja2FSigH+Gm6EG3cBKylVCpafwQhbYbpdkP12 11 | p7KtsPGUWIwBAyRr1AUqx0ceIafa9wIDAQABo4IC5TCCAuEwHwYDVR0jBBgwFoAU 12 | wDFSzVpQw4J8dHHOy+mc+XrrguIwHQYDVR0OBBYEFFgCnhEuuG5ptVcvPfBJZssU 13 | U6jFMBcGA1UdEQQQMA6CDCouZG9ja2VyLmNvbTATBgNVHSAEDDAKMAgGBmeBDAEC 14 | ATAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC 15 | MDsGA1UdHwQ0MDIwMKAuoCyGKmh0dHA6Ly9jcmwucjJtMDIuYW1hem9udHJ1c3Qu 16 | Y29tL3IybTAyLmNybDB1BggrBgEFBQcBAQRpMGcwLQYIKwYBBQUHMAGGIWh0dHA6 17 | Ly9vY3NwLnIybTAyLmFtYXpvbnRydXN0LmNvbTA2BggrBgEFBQcwAoYqaHR0cDov 18 | L2NydC5yMm0wMi5hbWF6b250cnVzdC5jb20vcjJtMDIuY2VyMAwGA1UdEwEB/wQC 19 | MAAwggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB3AN3cyjSV1+EWBeeVMvrHn/g9 20 | HFDf2wA6FBJ2Ciysu8gqAAABkWTCKI4AAAQDAEgwRgIhALzND2kn+PR4aObDiWGv 21 | 7QJZ/z4i/vhAGjd/vp2hjt4HAiEAytgsAcPUFd/Y98n5EQkMg6GBSUUygBMc7+Xk 22 | UVJXFgcAdQDm0jFjQHeMwRBBBtdxuc7B0kD2loSG+7qHMh39HjeOUAAAAZFkwiic 23 | AAAEAwBGMEQCIGXbP5ughe+EksczYEErOqY1LZHi9/SmXcp6/vAeuXBmAiB16Sw7 24 | 1sPB/CpKq75/pEwb5pamTdmeEIaHtGQTkTMZ5QB2AMz7D2qFcQll/pWbU87psnwi 25 | 6YVcDZeNtql+VMD+TA2wAAABkWTCKIoAAAQDAEcwRQIgX2WsD3ThDwalMCYqmf+X 26 | ICS9imyYmwKXJbfkSnMH/HkCIQCEkDrOAJZG1fGfyOzhSPHWPMxbuGwV18jBGe52 27 | D9PPQTANBgkqhkiG9w0BAQsFAAOCAQEAcW8Z3+JzqfGTYX+MBk2pQvo8msx+fINU 28 | ZzORUcFUNV0467G/Kc780S8GRxF8dr90WaFctMvw8yDOipfj0sacGJFVFFf5XoWu 29 | 1EqYyx3hgMLCQ5DzjAoY4X1KsfPRBe2DCsqf+Nt/TVzgVjOSglURBWKV2T+Av78H 30 | HiMjtEUZbatvuKvUg5S26dvUxXseN/8Jbbt9MCCGascPOf+zXGysoBSwglt0Nz7W 31 | hjXnY+BPeLD38ouUGx0R+sZbXICR6BIxtT4GgOcmJJ/KT3fcMObhsihiq6lO1aql 32 | Yo4AvWpg2tJtwFrG4ooDRyyARljHMUMIkuTz9Dypl/dnJJfzAQLjVA== 33 | -----END CERTIFICATE----- 34 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/testdata/influxdb-certificate.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw 3 | TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh 4 | cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 5 | WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu 6 | ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY 7 | MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc 8 | h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ 9 | 0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U 10 | A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW 11 | T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH 12 | B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC 13 | B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv 14 | KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn 15 | OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn 16 | jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw 17 | qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI 18 | rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV 19 | HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq 20 | hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL 21 | ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ 22 | 3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK 23 | NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 24 | ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur 25 | TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC 26 | jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc 27 | oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq 28 | 4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA 29 | mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d 30 | emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/write/WriteOptionsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.write; 23 | 24 | 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | 28 | import org.assertj.core.api.Assertions; 29 | import org.junit.jupiter.api.BeforeEach; 30 | import org.junit.jupiter.api.Test; 31 | 32 | import com.influxdb.v3.client.config.ClientConfig; 33 | 34 | 35 | class WriteOptionsTest { 36 | 37 | private ClientConfig.Builder configBuilder; 38 | 39 | @BeforeEach 40 | void before() { 41 | configBuilder = new ClientConfig.Builder() 42 | .host("http://localhost:8086") 43 | .token("my-token".toCharArray()); 44 | } 45 | 46 | 47 | @Test 48 | void optionsBasics() { 49 | WriteOptions options = new WriteOptions("my-database", WritePrecision.S, 512); 50 | 51 | Assertions.assertThat(options).isEqualTo(options); 52 | Assertions.assertThat(options).isNotEqualTo(null); 53 | Assertions.assertThat(options).isNotEqualTo(this); 54 | } 55 | 56 | @Test 57 | void optionsEqualAll() { 58 | WriteOptions options = new WriteOptions("my-database", WritePrecision.S, 512, true); 59 | WriteOptions optionsViaBuilder = new WriteOptions.Builder() 60 | .database("my-database").precision(WritePrecision.S).gzipThreshold(512).noSync(true).build(); 61 | 62 | Assertions.assertThat(options).isEqualTo(optionsViaBuilder); 63 | } 64 | 65 | @Test 66 | void optionsWithDefaultTags() { 67 | Map defaultTags = Map.of("unit", "U2", "model", "M1"); 68 | 69 | WriteOptions options = new WriteOptions("my-database", WritePrecision.S, 512, defaultTags); 70 | WriteOptions optionsViaBuilder = new WriteOptions.Builder() 71 | .database("my-database") 72 | .precision(WritePrecision.S) 73 | .gzipThreshold(512) 74 | .defaultTags(defaultTags) 75 | .build(); 76 | 77 | Assertions.assertThat(options).isEqualTo(optionsViaBuilder); 78 | } 79 | 80 | @Test 81 | void optionsWithHeaders() { 82 | Map headers = Map.of("X-Trace-Id", "189"); 83 | 84 | WriteOptions options = new WriteOptions("mydb", WritePrecision.MS, 2048, null, headers); 85 | WriteOptions optionsViaBuilder = new WriteOptions.Builder() 86 | .database("mydb") 87 | .precision(WritePrecision.MS) 88 | .gzipThreshold(2048) 89 | .headers(headers) 90 | .build(); 91 | 92 | Assertions.assertThat(options).isEqualTo(optionsViaBuilder); 93 | } 94 | 95 | @Test 96 | void optionsEmpty() { 97 | ClientConfig config = configBuilder 98 | .database("my-database") 99 | .organization("my-org") 100 | .writePrecision(WritePrecision.S) 101 | .gzipThreshold(512) 102 | .build(); 103 | 104 | WriteOptions options = new WriteOptions.Builder().build(); 105 | 106 | Assertions.assertThat(options.databaseSafe(config)).isEqualTo("my-database"); 107 | Assertions.assertThat(options.precisionSafe(config)).isEqualTo(WritePrecision.S); 108 | Assertions.assertThat(options.gzipThresholdSafe(config)).isEqualTo(512); 109 | } 110 | 111 | @Test 112 | void optionsOverrideAll() { 113 | ClientConfig config = configBuilder 114 | .database("my-database") 115 | .organization("my-org") 116 | .writePrecision(WritePrecision.S) 117 | .gzipThreshold(512) 118 | .writeNoSync(false) 119 | .build(); 120 | 121 | WriteOptions options = new WriteOptions("your-database", WritePrecision.US, 4096, true); 122 | 123 | Assertions.assertThat(options.databaseSafe(config)).isEqualTo("your-database"); 124 | Assertions.assertThat(options.precisionSafe(config)).isEqualTo(WritePrecision.US); 125 | Assertions.assertThat(options.gzipThresholdSafe(config)).isEqualTo(4096); 126 | Assertions.assertThat(options.noSyncSafe(config)).isEqualTo(true); 127 | } 128 | 129 | @Test 130 | void optionsOverrideDatabase() { 131 | ClientConfig config = configBuilder 132 | .database("my-database") 133 | .organization("my-org") 134 | .writePrecision(WritePrecision.S) 135 | .gzipThreshold(512) 136 | .build(); 137 | 138 | WriteOptions options = new WriteOptions.Builder().database("your-database").build(); 139 | 140 | Assertions.assertThat(options.databaseSafe(config)).isEqualTo("your-database"); 141 | Assertions.assertThat(options.precisionSafe(config)).isEqualTo(WritePrecision.S); 142 | Assertions.assertThat(options.gzipThresholdSafe(config)).isEqualTo(512); 143 | } 144 | 145 | @Test 146 | void optionsOverridePrecision() { 147 | ClientConfig config = configBuilder 148 | .database("my-database") 149 | .organization("my-org") 150 | .writePrecision(WritePrecision.US) 151 | .gzipThreshold(512) 152 | .build(); 153 | 154 | WriteOptions options = new WriteOptions.Builder().precision(WritePrecision.US).build(); 155 | 156 | Assertions.assertThat(options.databaseSafe(config)).isEqualTo("my-database"); 157 | Assertions.assertThat(options.precisionSafe(config)).isEqualTo(WritePrecision.US); 158 | Assertions.assertThat(options.gzipThresholdSafe(config)).isEqualTo(512); 159 | } 160 | 161 | @Test 162 | void optionsOverrideGzipThreshold() { 163 | ClientConfig config = configBuilder 164 | .database("my-database") 165 | .organization("my-org") 166 | .writePrecision(WritePrecision.S) 167 | .gzipThreshold(512) 168 | .build(); 169 | 170 | WriteOptions options = new WriteOptions.Builder().gzipThreshold(4096).build(); 171 | 172 | Assertions.assertThat(options.databaseSafe(config)).isEqualTo("my-database"); 173 | Assertions.assertThat(options.precisionSafe(config)).isEqualTo(WritePrecision.S); 174 | Assertions.assertThat(options.gzipThresholdSafe(config)).isEqualTo(4096); 175 | } 176 | 177 | @Test 178 | void optionsOverrideWriteNoSync() { 179 | ClientConfig config = configBuilder 180 | .database("my-database") 181 | .organization("my-org") 182 | .writeNoSync(true) 183 | .build(); 184 | 185 | WriteOptions options = new WriteOptions.Builder().noSync(false).build(); 186 | 187 | Assertions.assertThat(options.noSyncSafe(config)).isEqualTo(false); 188 | } 189 | 190 | @Test 191 | void optionsOverridesDefaultTags() { 192 | Map defaultTagsBase = new HashMap<>() {{ 193 | put("model", "train"); 194 | put("scale", "HO"); 195 | }}; 196 | 197 | Map defaultTagsNew = new HashMap<>() {{ 198 | put("unit", "D1"); 199 | }}; 200 | 201 | ClientConfig config = configBuilder 202 | .database("my-database") 203 | .organization("my-org") 204 | .writePrecision(WritePrecision.S) 205 | .gzipThreshold(512) 206 | .defaultTags(defaultTagsBase) 207 | .build(); 208 | 209 | Assertions.assertThat(config.getDefaultTags()).isEqualTo(defaultTagsBase); 210 | 211 | WriteOptions options = new WriteOptions.Builder() 212 | .defaultTags(defaultTagsNew) 213 | .build(); 214 | 215 | Assertions.assertThat(options.databaseSafe(config)).isEqualTo("my-database"); 216 | Assertions.assertThat(options.precisionSafe(config)).isEqualTo(WritePrecision.S); 217 | Assertions.assertThat(options.gzipThresholdSafe(config)).isEqualTo(512); 218 | Assertions.assertThat(options.defaultTagsSafe(config)).isEqualTo(defaultTagsNew); 219 | } 220 | 221 | @Test 222 | void optionsOverridesEmptyDefaultTags() { 223 | 224 | Map defaultTags = new HashMap<>() {{ 225 | put("model", "train"); 226 | put("scale", "HO"); 227 | }}; 228 | 229 | ClientConfig config = configBuilder 230 | .database("my-database") 231 | .organization("my-org") 232 | .writePrecision(WritePrecision.S) 233 | .gzipThreshold(512) 234 | .defaultTags(defaultTags) 235 | .build(); 236 | 237 | Assertions.assertThat(config.getDefaultTags()).isEqualTo(defaultTags); 238 | 239 | WriteOptions options = new WriteOptions.Builder().build(); 240 | 241 | Assertions.assertThat(options.databaseSafe(config)).isEqualTo("my-database"); 242 | Assertions.assertThat(options.precisionSafe(config)).isEqualTo(WritePrecision.S); 243 | Assertions.assertThat(options.gzipThresholdSafe(config)).isEqualTo(512); 244 | Assertions.assertThat(options.defaultTagsSafe(config)).isEqualTo(defaultTags); 245 | } 246 | 247 | @Test 248 | void optionsHashCode() { 249 | 250 | Map defaultTags = Map.of("unit", "U2", "model", "M1"); 251 | 252 | WriteOptions.Builder builder = new WriteOptions.Builder(); 253 | WriteOptions baseOptions = builder.build(); 254 | 255 | Assertions.assertThat(baseOptions.hashCode()) 256 | .isNotEqualTo(builder.database("my-database").build().hashCode()); 257 | Assertions.assertThat(baseOptions.hashCode()) 258 | .isNotEqualTo(builder.defaultTags(defaultTags).build().hashCode()); 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/write/WritePrecisionConverterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.write; 23 | 24 | import java.util.Map; 25 | 26 | import org.junit.jupiter.api.Test; 27 | 28 | import static org.junit.jupiter.api.Assertions.assertEquals; 29 | 30 | public class WritePrecisionConverterTest { 31 | @Test 32 | void toV2ApiString() { 33 | Map testCases = Map.of( 34 | WritePrecision.NS, "ns", 35 | WritePrecision.US, "us", 36 | WritePrecision.MS, "ms", 37 | WritePrecision.S, "s" 38 | ); 39 | 40 | for (Map.Entry e : testCases.entrySet()) { 41 | WritePrecision precision = e.getKey(); 42 | String expectedString = e.getValue(); 43 | String result = WritePrecisionConverter.toV2ApiString(precision); 44 | assertEquals(expectedString, result, "Failed for precision: " + precision); 45 | } 46 | } 47 | 48 | @Test 49 | void toV3ApiString() { 50 | Map tc = Map.of( 51 | WritePrecision.NS, "nanosecond", 52 | WritePrecision.US, "microsecond", 53 | WritePrecision.MS, "millisecond", 54 | WritePrecision.S, "second" 55 | ); 56 | 57 | for (Map.Entry e : tc.entrySet()) { 58 | WritePrecision precision = e.getKey(); 59 | String expectedString = e.getValue(); 60 | String result = WritePrecisionConverter.toV3ApiString(precision); 61 | assertEquals(expectedString, result, "Failed for precision: " + precision); 62 | } 63 | } 64 | } 65 | --------------------------------------------------------------------------------