├── .bazelrc ├── .bazelversion ├── .factory └── automation.yml ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── BUG_REPORT.md │ ├── FEATURE_REQUEST.md │ └── REFACTOR.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── BUILD ├── LICENSE ├── README.md ├── RELEASE_TEMPLATE.md ├── VERSION ├── WORKSPACE ├── dependencies ├── ide │ └── rust │ │ ├── BUILD │ │ └── sync.sh └── vaticle │ ├── BUILD │ ├── artifacts.bzl │ └── repositories.bzl ├── deployment.bzl ├── rustfmt.toml ├── src ├── answer │ ├── concept_map.rs │ ├── mod.rs │ └── numeric.rs ├── common │ ├── address.rs │ ├── credential.rs │ ├── error.rs │ ├── id.rs │ ├── info.rs │ ├── mod.rs │ └── options.rs ├── concept │ └── mod.rs ├── connection │ ├── connection.rs │ ├── message.rs │ ├── mod.rs │ ├── network │ │ ├── channel.rs │ │ ├── mod.rs │ │ ├── proto │ │ │ ├── common.rs │ │ │ ├── concept.rs │ │ │ ├── database.rs │ │ │ ├── message.rs │ │ │ └── mod.rs │ │ ├── stub.rs │ │ └── transmitter │ │ │ ├── mod.rs │ │ │ ├── response_sink.rs │ │ │ ├── rpc.rs │ │ │ └── transaction.rs │ ├── runtime.rs │ └── transaction_stream.rs ├── database │ ├── database.rs │ ├── database_manager.rs │ ├── mod.rs │ ├── query.rs │ ├── session.rs │ └── transaction.rs └── lib.rs ├── tests ├── BUILD ├── behaviour │ ├── connection │ │ ├── database │ │ │ ├── mod.rs │ │ │ └── steps.rs │ │ ├── mod.rs │ │ ├── session │ │ │ ├── mod.rs │ │ │ └── steps.rs │ │ ├── steps.rs │ │ └── transaction │ │ │ ├── mod.rs │ │ │ └── steps.rs │ ├── mod.rs │ ├── session_tracker.rs │ ├── typeql │ │ ├── mod.rs │ │ └── steps.rs │ └── util.rs ├── integration │ ├── common.rs │ ├── mod.rs │ ├── queries.rs │ └── runtimes.rs └── tests.rs └── tools ├── start-cluster-servers.sh ├── start-core-server.sh ├── stop-cluster-servers.sh └── stop-core-server.sh /.bazelrc: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2022 Vaticle 3 | # 4 | # Licensed to the Apache Software Foundation (ASF) under one 5 | # or more contributor license agreements. See the NOTICE file 6 | # distributed with this work for additional information 7 | # regarding copyright ownership. The ASF licenses this file 8 | # to you under the Apache License, Version 2.0 (the 9 | # "License"); you may not use this file except in compliance 10 | # with the License. You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, 15 | # software distributed under the License is distributed on an 16 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | # KIND, either express or implied. See the License for the 18 | # specific language governing permissions and limitations 19 | # under the License. 20 | # 21 | 22 | try-import ./.bazel-remote-cache.rc 23 | -------------------------------------------------------------------------------- /.bazelversion: -------------------------------------------------------------------------------- 1 | 5.1.1 2 | -------------------------------------------------------------------------------- /.factory/automation.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2022 Vaticle 3 | # 4 | # Licensed to the Apache Software Foundation (ASF) under one 5 | # or more contributor license agreements. See the NOTICE file 6 | # distributed with this work for additional information 7 | # regarding copyright ownership. The ASF licenses this file 8 | # to you under the Apache License, Version 2.0 (the 9 | # "License"); you may not use this file except in compliance 10 | # with the License. You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, 15 | # software distributed under the License is distributed on an 16 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | # KIND, either express or implied. See the License for the 18 | # specific language governing permissions and limitations 19 | # under the License. 20 | # 21 | 22 | config: 23 | version-candidate: VERSION 24 | dependencies: 25 | dependencies: [build] 26 | typedb-common: [build] 27 | typedb-protocol: [build, release] 28 | typeql: [build, release] 29 | 30 | build: 31 | quality: 32 | filter: 33 | owner: vaticle 34 | branch: master 35 | dependency-analysis: 36 | image: vaticle-ubuntu-22.04 37 | command: | 38 | bazel run @vaticle_dependencies//factory/analysis:dependency-analysis 39 | correctness: 40 | build: 41 | image: vaticle-ubuntu-22.04 42 | command: | 43 | export ARTIFACT_USERNAME=$REPO_VATICLE_USERNAME 44 | export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD 45 | bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh 46 | bazel run @vaticle_dependencies//distribution/artifact:create-netrc 47 | bazel build //... 48 | bazel run @vaticle_dependencies//tool/checkstyle:test-coverage 49 | bazel test $(bazel query 'kind(checkstyle_test, //...)') --test_output=errors 50 | bazel test $(bazel query 'kind(rustfmt_test, //...)') --@rules_rust//:rustfmt.toml=//:rustfmt_config 51 | test-integration-core: 52 | image: vaticle-ubuntu-22.04 53 | dependencies: 54 | - build 55 | command: | 56 | export ARTIFACT_USERNAME=$REPO_VATICLE_USERNAME 57 | export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD 58 | bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh 59 | bazel run @vaticle_dependencies//distribution/artifact:create-netrc 60 | bazel build //... 61 | tools/start-core-server.sh 62 | bazel test //tests --test_arg=-- --test_arg=integration::queries::core --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 63 | tools/stop-core-server.sh 64 | exit $TEST_SUCCESS 65 | test-integration-cluster: 66 | machine: 4-core-16-gb 67 | image: vaticle-ubuntu-22.04 68 | dependencies: 69 | - build 70 | command: | 71 | export ARTIFACT_USERNAME=$REPO_VATICLE_USERNAME 72 | export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD 73 | bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh 74 | bazel run @vaticle_dependencies//distribution/artifact:create-netrc 75 | bazel build //... 76 | source tools/start-cluster-servers.sh # use source to receive export vars 77 | bazel test //tests --test_env=ROOT_CA=$ROOT_CA --test_arg=-- --test_arg=integration::queries::cluster --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 78 | tools/stop-cluster-servers.sh 79 | exit $TEST_SUCCESS 80 | test-integration-runtimes: 81 | image: vaticle-ubuntu-22.04 82 | dependencies: 83 | - build 84 | command: | 85 | export ARTIFACT_USERNAME=$REPO_VATICLE_USERNAME 86 | export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD 87 | bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh 88 | bazel run @vaticle_dependencies//distribution/artifact:create-netrc 89 | bazel build //... 90 | source tools/start-cluster-servers.sh # use source to receive export vars 91 | bazel test //tests --test_env=ROOT_CA=$ROOT_CA --test_arg=-- --test_arg=integration::runtimes --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 92 | tools/stop-cluster-servers.sh 93 | exit $TEST_SUCCESS 94 | test-behaviour-connection: 95 | image: vaticle-ubuntu-22.04 96 | dependencies: 97 | - build 98 | command: | 99 | export ARTIFACT_USERNAME=$REPO_VATICLE_USERNAME 100 | export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD 101 | bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh 102 | bazel run @vaticle_dependencies//distribution/artifact:create-netrc 103 | bazel build //... 104 | tools/start-core-server.sh 105 | bazel test //tests --test_arg=-- --test_arg=behaviour::connection --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 106 | tools/stop-core-server.sh 107 | exit $TEST_SUCCESS 108 | deploy-crate-snapshot: 109 | filter: 110 | owner: vaticle 111 | branch: master 112 | image: vaticle-ubuntu-22.04 113 | dependencies: 114 | - build 115 | - test-integration-core 116 | - test-integration-cluster 117 | - test-integration-runtimes 118 | - test-behaviour-connection 119 | command: | 120 | export DEPLOY_CRATE_TOKEN=$REPO_VATICLE_CRATES_TOKEN 121 | bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh 122 | bazel run --define version=$(git rev-parse HEAD) //:deploy_crate -- snapshot 123 | 124 | release: 125 | filter: 126 | owner: vaticle 127 | branch: master 128 | deployment: 129 | deploy-github: 130 | image: vaticle-ubuntu-22.04 131 | command: | 132 | export PYENV_ROOT="/opt/pyenv" 133 | pyenv install 3.7.9 134 | pyenv global 3.7.9 135 | sudo unlink /usr/bin/python3 136 | sudo ln -s $(which python3) /usr/bin/python3 137 | sudo ln -s /usr/share/pyshared/lsb_release.py /opt/pyenv/versions/3.7.9/lib/python3.7/site-packages/lsb_release.py 138 | python3 -m pip install certifi 139 | export NOTES_CREATE_TOKEN=$REPO_GITHUB_TOKEN 140 | bazel run @vaticle_dependencies//tool/release/notes:create -- $FACTORY_OWNER $FACTORY_REPO $FACTORY_COMMIT $(cat VERSION) ./RELEASE_TEMPLATE.md 141 | export DEPLOY_GITHUB_TOKEN=$REPO_GITHUB_TOKEN 142 | bazel run --define version=$(cat VERSION) //:deploy_github -- $FACTORY_COMMIT 143 | deploy-crate-release: 144 | image: vaticle-ubuntu-22.04 145 | command: | 146 | export DEPLOY_CRATE_TOKEN=$REPO_CRATES_TOKEN 147 | bazel run --define version=$(cat VERSION) //:deploy_crate -- release 148 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | /* @benjaminasmall 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG_REPORT.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Report a bug here, or visit forum.vaticle.com for troubleshooting discussions 4 | labels: bug 5 | --- 6 | 7 | Please replace every line in curly brackets { like this } with an appropriate description, and remove this line. 8 | 9 | ## Description 10 | 11 | { Please provide a clear and concise description of the bug. } 12 | 13 | ## Environment 14 | 15 | 1. OS (where TypeDB Client runs): { e.g. MacOS 10, Windows 10, Ubuntu 16.4, etc. } 16 | 2. TypeDB version (and platform): { e.g. TypeDB 2.11.1, or TypeDB Cluster 2.11.1 on Google Cloud } 17 | 3. TypeDB Client version: { e.g. typedb-client 2.11.1 } 18 | 4. Other environment details: 19 | 20 | ## Reproducible Steps 21 | 22 | Steps to create the smallest reproducible scenario: 23 | 1. { e.g. Run ... } 24 | 2. { e.g. Load ... } 25 | 3. { e.g. Query ... } 26 | 4. { e.g. See error ... } 27 | 28 | ## Expected Output 29 | 30 | { Please describe what you expected to happen. } 31 | 32 | ## Actual Output 33 | 34 | { Please describe what actually happened. } 35 | 36 | ## Additional information 37 | 38 | { Any additional information, including logs or screenshots if you have any. } 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Request a feature here, or visit forum.vaticle.com for ideas and questions 4 | labels: feature 5 | --- 6 | 7 | Please replace every line in curly brackets { like this } with an appropriate description, and remove this line. 8 | 9 | ## Problem to Solve 10 | 11 | { Please describe the problem you would like to solve. } 12 | 13 | ## Current Workaround 14 | 15 | { Please describe how you currently solve or work around this problem, given TypeDB's limitation. } 16 | 17 | ## Proposed Solution 18 | 19 | { Please describe the solution you would like TypeDB to provide, to solve the problem above. } 20 | 21 | ## Additional Information 22 | 23 | { Any additional information, including logs or screenshots if you have any. } 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/REFACTOR.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Refactor 3 | about: Propose an architecture refactor here 4 | labels: refactor 5 | --- 6 | 7 | Please replace every line in curly brackets { like this } with appropriate answers, and remove this line. 8 | 9 | ## Problem to Solve 10 | 11 | { Please describe the problem with the current architecture that you would like to solve. } 12 | 13 | ## Proposed Solution 14 | 15 | { Please describe how you would like to change the architecture, to solve the problem above. } 16 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **For the title of this PR:** please follow the grammatical rules of a usual publication title, without capitalisation (except for the first letter). Thus, the title should NOT CONTAIN CODE: no dots, no parentheses, no backticks, no brackets, etc. It needs to be distinctive (not detailed) and succinct (not lengthy). Details of this PR will go in the description. **For the description of this PR:** please replace every line in curly brackets ( { like this } ) with an appropriate description following the guidance. Finally, **please remove this paragraph**. 2 | 3 | ## What is the goal of this PR? 4 | 5 | { In the form of a paragraph (only use bullet points if strictly necessary), please describe the goal of this PR, why they are valuable to achieve, and reference the related GitHub issues. This section will be automatically compiled into the release notes, so please: 6 | - describe the impact of the change in this PR to the _user_ of this repository (e.g. end user, contributor, developer). 7 | - describe the new product behaviour in _present tense_, and the old behaviour and how it's been changed in _past tense_. 8 | - Use the _Royal We_: _"We"_ made changes, not _"I"_ made changes. } 9 | 10 | ## What are the changes implemented in this PR? 11 | 12 | { Please explain what you implemented, why your changes are the best way to achieve the goal(s) above. Please describe every method, class and package, by explaining: 13 | - its responsibility, 14 | - how it's expected to behave, and 15 | - how it relates to the adjacent methods/classes/packages it interacts with. 16 | 17 | This would allow the reviewer to understand your intentions in the code much better. If you're adding new classes, make sure these explanations are also included in the class header comments. Last but not least, please reference the GitHub issues to be automatically closed, such like 'closes #number'. } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2022 Vaticle 3 | # 4 | # Licensed to the Apache Software Foundation (ASF) under one 5 | # or more contributor license agreements. See the NOTICE file 6 | # distributed with this work for additional information 7 | # regarding copyright ownership. The ASF licenses this file 8 | # to you under the Apache License, Version 2.0 (the 9 | # "License"); you may not use this file except in compliance 10 | # with the License. You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, 15 | # software distributed under the License is distributed on an 16 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | # KIND, either express or implied. See the License for the 18 | # specific language governing permissions and limitations 19 | # under the License. 20 | # 21 | 22 | .DS_Store 23 | .ijwb 24 | bazel-* 25 | .bazel-remote-cache.rc 26 | .bazel-cache-credential.json 27 | target 28 | Cargo.lock 29 | Cargo.toml 30 | -------------------------------------------------------------------------------- /BUILD: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2022 Vaticle 3 | # 4 | # Licensed to the Apache Software Foundation (ASF) under one 5 | # or more contributor license agreements. See the NOTICE file 6 | # distributed with this work for additional information 7 | # regarding copyright ownership. The ASF licenses this file 8 | # to you under the Apache License, Version 2.0 (the 9 | # "License"); you may not use this file except in compliance 10 | # with the License. You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, 15 | # software distributed under the License is distributed on an 16 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | # KIND, either express or implied. See the License for the 18 | # specific language governing permissions and limitations 19 | # under the License. 20 | # 21 | 22 | package(default_visibility = ["//visibility:public"]) 23 | 24 | load("@rules_rust//rust:defs.bzl", "rust_library", "rustfmt_test") 25 | load("@vaticle_bazel_distribution//crates:rules.bzl", "assemble_crate", "deploy_crate") 26 | load("@vaticle_bazel_distribution//github:rules.bzl", "deploy_github") 27 | load("@vaticle_dependencies//distribution:deployment.bzl", "deployment") 28 | load("@vaticle_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") 29 | load("//:deployment.bzl", deployment_github = "deployment") 30 | 31 | rust_library( 32 | name = "typedb_client", 33 | srcs = glob(["src/**/*.rs"]), 34 | tags = ["crate-name=typedb-client"], 35 | deps = [ 36 | "@crates//:chrono", 37 | "@crates//:crossbeam", 38 | "@crates//:futures", 39 | "@crates//:http", 40 | "@crates//:itertools", 41 | "@crates//:log", 42 | "@crates//:prost", 43 | "@crates//:tokio", 44 | "@crates//:tokio-stream", 45 | "@crates//:tonic", 46 | "@crates//:uuid", 47 | "@vaticle_typedb_protocol//grpc/rust:typedb_protocol", 48 | "@vaticle_typeql//rust:typeql_lang", 49 | ], 50 | ) 51 | 52 | assemble_crate( 53 | name = "assemble_crate", 54 | description = "TypeDB Client API for Rust", 55 | homepage = "https://github.com/vaticle/typedb-client-rust", 56 | license = "Apache-2.0", 57 | repository = "https://github.com/vaticle/typedb-client-rust", 58 | target = "typedb_client", 59 | ) 60 | 61 | deploy_crate( 62 | name = "deploy_crate", 63 | release = deployment["crate.release"], 64 | snapshot = deployment["crate.snapshot"], 65 | target = ":assemble_crate", 66 | ) 67 | 68 | deploy_github( 69 | name = "deploy_github", 70 | draft = True, 71 | organisation = deployment_github["github.organisation"], 72 | release_description = "//:RELEASE_TEMPLATE.md", 73 | repository = deployment_github["github.repository"], 74 | title = "TypeDB Client Rust", 75 | title_append_version = True, 76 | ) 77 | 78 | checkstyle_test( 79 | name = "checkstyle", 80 | size = "small", 81 | include = glob([ 82 | "*", 83 | "src/**/*", 84 | "tools/*", 85 | ".factory/*", 86 | ]), 87 | exclude = glob([ 88 | "*.md", 89 | ".bazelversion", 90 | ".bazel-remote-cache.rc", 91 | ".bazel-cache-credential.json", 92 | "LICENSE", 93 | "VERSION", 94 | ]), 95 | license_type = "apache-header", 96 | ) 97 | 98 | checkstyle_test( 99 | name = "checkstyle-license", 100 | size = "small", 101 | include = ["LICENSE"], 102 | license_type = "apache-fulltext", 103 | ) 104 | 105 | filegroup( 106 | name = "rustfmt_config", 107 | srcs = ["rustfmt.toml"], 108 | ) 109 | 110 | rustfmt_test( 111 | name = "client_rustfmt_test", 112 | targets = ["typedb_client"], 113 | ) 114 | 115 | # CI targets that are not declared in any BUILD file, but are called externally 116 | filegroup( 117 | name = "ci", 118 | data = [ 119 | "@vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh", 120 | "@vaticle_dependencies//tool/cargo:sync", 121 | ], 122 | ) 123 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # THIS REPOSITORY HAS BEEN ARCHIVED. Please visit: [@vaticle/typedb-driver/rust](https://github.com/vaticle/typedb-driver/tree/development/rust) 2 | 3 | --- 4 | 5 | # TypeDB Client for Rust 6 | 7 | [![Factory](https://factory.vaticle.com/api/status/vaticle/typedb-client-rust/badge.svg)](https://factory.vaticle.com/vaticle/typedb-client-rust) 8 | [![Discord](https://img.shields.io/discord/665254494820368395?color=7389D8&label=chat&logo=discord&logoColor=ffffff)](https://vaticle.com/discord) 9 | [![Discussion Forum](https://img.shields.io/discourse/https/forum.vaticle.com/topics.svg)](https://forum.vaticle.com) 10 | [![Stack Overflow](https://img.shields.io/badge/stackoverflow-typedb-796de3.svg)](https://stackoverflow.com/questions/tagged/typedb) 11 | [![Stack Overflow](https://img.shields.io/badge/stackoverflow-typeql-3dce8c.svg)](https://stackoverflow.com/questions/tagged/typeql) 12 | 13 | ## Project Status 14 | This is a **work in progress** and is not yet suitable for production usage. 15 | 16 | It can connect to TypeDB, run read and write queries, and return answers. Concept API methods are not available yet. 17 | 18 | ## Client Architecture 19 | To learn about the mechanism that a TypeDB Client uses to set up communication with databases running on the TypeDB Server, refer to [TypeDB > Client API > Overview](http://docs.vaticle.com/docs/client-api/overview). 20 | 21 | The TypeDB Client for Rust provides a fully async API that supports the [`tokio`](https://crates.io/crates/tokio) **multi-threaded** runtime. 22 | 23 | ## Quickstart 24 | 1. Import `typedb-client` through Cargo: 25 | ```toml 26 | typedb-client = "0.1.2" 27 | ``` 28 | 2. Make sure the [TypeDB Server](https://docs.vaticle.com/docs/running-typedb/install-and-run#start-the-typedb-server) is running. 29 | 3. See `tests/integration` for examples of usage. 30 | 31 | ## Build from Source 32 | > Note: You don't need to compile TypeDB Client from source if you just want to use it in your code. See the _"Quickstart"_ section above. 33 | 34 | 1. Make sure you have [Bazel](https://docs.bazel.build/versions/master/install.html) installed on your machine. 35 | 36 | 2. Build the library: 37 | 38 | a) to build the native/raw rlib: 39 | ``` 40 | bazel build //:typedb_client 41 | ``` 42 | The rlib will be produced at: `bazel-bin/libtypedb_client-{hash}.rlib`. 43 | 44 | b) to build the crate for a Cargo project: 45 | ``` 46 | bazel build //:assemble_crate 47 | ``` 48 | The Cargo crate will be produced at: 49 | ``` 50 | bazel-bin/assemble_crate.crate 51 | ``` 52 | You can then unzip this crate to retrieve `Cargo.toml`. **Please note**: this process has not yet been thoroughly tested. The generated `Cargo.toml` may not be fully correct. See the `Cargo.toml` of the `typedb-client` crate for reference. 53 | 54 | -------------------------------------------------------------------------------- /RELEASE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Documentation: https://github.com/vaticle/typedb-client-rust/blob/master/README.md 2 | 3 | ## Project Status 4 | This is a **work in progress** and is not yet suitable for production usage. 5 | 6 | It can connect to TypeDB, run read and write queries, and return answers. Concept API methods are not available yet. 7 | 8 | ## Distribution 9 | Import [`typedb-client`](https://crates.io/crates/typedb-client) through Cargo: 10 | ```toml 11 | typedb-client = "{version}" 12 | ``` 13 | 14 | { release notes } 15 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.1.3 2 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2022 Vaticle 3 | # 4 | # Licensed to the Apache Software Foundation (ASF) under one 5 | # or more contributor license agreements. See the NOTICE file 6 | # distributed with this work for additional information 7 | # regarding copyright ownership. The ASF licenses this file 8 | # to you under the Apache License, Version 2.0 (the 9 | # "License"); you may not use this file except in compliance 10 | # with the License. You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, 15 | # software distributed under the License is distributed on an 16 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | # KIND, either express or implied. See the License for the 18 | # specific language governing permissions and limitations 19 | # under the License. 20 | # 21 | 22 | load("//dependencies/vaticle:repositories.bzl", "vaticle_dependencies") 23 | vaticle_dependencies() 24 | 25 | # Load //builder/java 26 | load("@vaticle_dependencies//builder/java:deps.bzl", java_deps = "deps") 27 | java_deps() 28 | 29 | # Load //builder/kotlin 30 | load("@vaticle_dependencies//builder/kotlin:deps.bzl", kotlin_deps = "deps") 31 | kotlin_deps() 32 | load("@io_bazel_rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") 33 | kotlin_repositories() 34 | load("@io_bazel_rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") 35 | kt_register_toolchains() 36 | 37 | # Load //builder/grpc (required by @vaticle_typedb_protocol) 38 | load("@vaticle_dependencies//builder/grpc:deps.bzl", grpc_deps = "deps") 39 | grpc_deps() 40 | load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", 41 | com_github_grpc_grpc_deps = "grpc_deps") 42 | com_github_grpc_grpc_deps() 43 | 44 | # Load //builder/rust 45 | load("@vaticle_dependencies//builder/rust:deps.bzl", rust_deps = "deps") 46 | rust_deps() 47 | 48 | load("@rules_rust//rust:repositories.bzl", "rules_rust_dependencies", "rust_register_toolchains") 49 | rules_rust_dependencies() 50 | rust_register_toolchains(edition = "2021", include_rustc_srcs = True) 51 | 52 | load("@vaticle_dependencies//library/crates:crates.bzl", "fetch_crates") 53 | fetch_crates() 54 | load("@crates//:defs.bzl", "crate_repositories") 55 | crate_repositories() 56 | 57 | # Load //builder/python 58 | load("@vaticle_dependencies//builder/python:deps.bzl", python_deps = "deps") 59 | python_deps() 60 | 61 | # Load //tool/checkstyle 62 | load("@vaticle_dependencies//tool/checkstyle:deps.bzl", checkstyle_deps = "deps") 63 | checkstyle_deps() 64 | 65 | # Load //tool/common 66 | load("@vaticle_dependencies//tool/common:deps.bzl", "vaticle_dependencies_ci_pip") 67 | vaticle_dependencies_ci_pip() 68 | 69 | ###################################### 70 | # Load @vaticle_bazel_distribution # 71 | ###################################### 72 | 73 | load("@vaticle_dependencies//distribution:deps.bzl", "vaticle_bazel_distribution") 74 | vaticle_bazel_distribution() 75 | 76 | # Load //github 77 | load("@vaticle_bazel_distribution//github:deps.bzl", github_deps = "deps") 78 | github_deps() 79 | 80 | ################################ 81 | # Load @vaticle dependencies # 82 | ################################ 83 | 84 | load("//dependencies/vaticle:repositories.bzl", "vaticle_typedb_common", "vaticle_typedb_protocol", "vaticle_typedb_behaviour", "vaticle_typeql") 85 | vaticle_typedb_common() 86 | vaticle_typedb_protocol() 87 | vaticle_typedb_behaviour() 88 | vaticle_typeql() 89 | 90 | # Load artifacts 91 | load("//dependencies/vaticle:artifacts.bzl", "vaticle_typedb_artifacts", "vaticle_typedb_cluster_artifacts") 92 | vaticle_typedb_artifacts() 93 | vaticle_typedb_cluster_artifacts() 94 | 95 | ############################ 96 | # Load @maven dependencies # 97 | ############################ 98 | 99 | load("@vaticle_dependencies//tool/common:deps.bzl", vaticle_dependencies_tool_maven_artifacts = "maven_artifacts") 100 | load("@vaticle_bazel_distribution//maven:deps.bzl", vaticle_bazel_distribution_maven_artifacts = "maven_artifacts") 101 | 102 | load("@vaticle_dependencies//library/maven:rules.bzl", "maven") 103 | maven(vaticle_dependencies_tool_maven_artifacts + vaticle_bazel_distribution_maven_artifacts) 104 | -------------------------------------------------------------------------------- /dependencies/ide/rust/BUILD: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2022 Vaticle 3 | # 4 | # Licensed to the Apache Software Foundation (ASF) under one 5 | # or more contributor license agreements. See the NOTICE file 6 | # distributed with this work for additional information 7 | # regarding copyright ownership. The ASF licenses this file 8 | # to you under the Apache License, Version 2.0 (the 9 | # "License"); you may not use this file except in compliance 10 | # with the License. You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, 15 | # software distributed under the License is distributed on an 16 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | # KIND, either express or implied. See the License for the 18 | # specific language governing permissions and limitations 19 | # under the License. 20 | # 21 | 22 | load("@vaticle_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") 23 | 24 | checkstyle_test( 25 | name = "checkstyle", 26 | include = glob(["*"]), 27 | license_type = "apache-header", 28 | size = "small", 29 | ) 30 | -------------------------------------------------------------------------------- /dependencies/ide/rust/sync.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright (C) 2022 Vaticle 4 | # 5 | # Licensed to the Apache Software Foundation (ASF) under one 6 | # or more contributor license agreements. See the NOTICE file 7 | # distributed with this work for additional information 8 | # regarding copyright ownership. The ASF licenses this file 9 | # to you under the Apache License, Version 2.0 (the 10 | # "License"); you may not use this file except in compliance 11 | # with the License. You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, 16 | # software distributed under the License is distributed on an 17 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | # KIND, either express or implied. See the License for the 19 | # specific language governing permissions and limitations 20 | # under the License. 21 | # 22 | 23 | bazel run @vaticle_dependencies//tool/cargo:sync 24 | -------------------------------------------------------------------------------- /dependencies/vaticle/BUILD: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2022 Vaticle 3 | # 4 | # Licensed to the Apache Software Foundation (ASF) under one 5 | # or more contributor license agreements. See the NOTICE file 6 | # distributed with this work for additional information 7 | # regarding copyright ownership. The ASF licenses this file 8 | # to you under the Apache License, Version 2.0 (the 9 | # "License"); you may not use this file except in compliance 10 | # with the License. You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, 15 | # software distributed under the License is distributed on an 16 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | # KIND, either express or implied. See the License for the 18 | # specific language governing permissions and limitations 19 | # under the License. 20 | # 21 | 22 | load("@vaticle_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") 23 | 24 | checkstyle_test( 25 | name = "checkstyle", 26 | include = glob(["*"]), 27 | license_type = "apache-header", 28 | size = "small", 29 | ) 30 | -------------------------------------------------------------------------------- /dependencies/vaticle/artifacts.bzl: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2022 Vaticle 3 | # 4 | # Licensed to the Apache Software Foundation (ASF) under one 5 | # or more contributor license agreements. See the NOTICE file 6 | # distributed with this work for additional information 7 | # regarding copyright ownership. The ASF licenses this file 8 | # to you under the Apache License, Version 2.0 (the 9 | # "License"); you may not use this file except in compliance 10 | # with the License. You may obtain a copy of the License at 11 | 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, 15 | # software distributed under the License is distributed on an 16 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | # KIND, either express or implied. See the License for the 18 | # specific language governing permissions and limitations 19 | # under the License. 20 | # 21 | 22 | load("@vaticle_dependencies//distribution/artifact:rules.bzl", "native_artifact_files") 23 | load("@vaticle_dependencies//distribution:deployment.bzl", "deployment", "deployment_private") 24 | 25 | def vaticle_typedb_artifacts(): 26 | native_artifact_files( 27 | name = "vaticle_typedb_artifact", 28 | group_name = "vaticle_typedb", 29 | artifact_name = "typedb-server-{platform}-{version}.{ext}", 30 | tag_source = deployment["artifact.release"], 31 | commit_source = deployment["artifact.snapshot"], 32 | tag = "2.14.0" 33 | ) 34 | 35 | def vaticle_typedb_cluster_artifacts(): 36 | native_artifact_files( 37 | name = "vaticle_typedb_cluster_artifact", 38 | group_name = "vaticle_typedb_cluster", 39 | artifact_name = "typedb-cluster-all-{platform}-{version}.{ext}", 40 | tag_source = deployment_private["artifact.release"], 41 | commit_source = deployment_private["artifact.snapshot"], 42 | tag = "2.14.1" 43 | ) 44 | -------------------------------------------------------------------------------- /dependencies/vaticle/repositories.bzl: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2022 Vaticle 3 | # 4 | # Licensed to the Apache Software Foundation (ASF) under one 5 | # or more contributor license agreements. See the NOTICE file 6 | # distributed with this work for additional information 7 | # regarding copyright ownership. The ASF licenses this file 8 | # to you under the Apache License, Version 2.0 (the 9 | # "License"); you may not use this file except in compliance 10 | # with the License. You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, 15 | # software distributed under the License is distributed on an 16 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | # KIND, either express or implied. See the License for the 18 | # specific language governing permissions and limitations 19 | # under the License. 20 | # 21 | 22 | load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") 23 | 24 | def vaticle_dependencies(): 25 | git_repository( 26 | name = "vaticle_dependencies", 27 | remote = "https://github.com/vaticle/dependencies", 28 | commit = "b968cab9c4d2f85d80a0069b8d15cce5afdd0da5", # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_dependencies 29 | ) 30 | 31 | def vaticle_typedb_common(): 32 | git_repository( 33 | name = "vaticle_typedb_common", 34 | remote = "https://github.com/vaticle/typedb-common", 35 | commit = "aa03cb5f6a57ec2a51291b7a0510734ca1f41479" # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_typedb_common 36 | ) 37 | 38 | def vaticle_typedb_protocol(): 39 | git_repository( 40 | name = "vaticle_typedb_protocol", 41 | remote = "https://github.com/vaticle/typedb-protocol", 42 | commit = "b1c19e02054c1a1d354b42875e6ccd67602a546f", # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_dependencies 43 | ) 44 | 45 | def vaticle_typedb_behaviour(): 46 | git_repository( 47 | name = "vaticle_typedb_behaviour", 48 | remote = "https://github.com/vaticle/typedb-behaviour", 49 | commit = "21ae86ac44e84c7dbb4351a9006b97a555f0a215", # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_typedb_behaviour 50 | ) 51 | 52 | def vaticle_typeql(): 53 | git_repository( 54 | name = "vaticle_typeql", 55 | remote = "https://github.com/vaticle/typeql", 56 | commit = "7a63699b3879296ae3039577ba3f5220bbf6d33d", # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_dependencies 57 | ) 58 | -------------------------------------------------------------------------------- /deployment.bzl: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2022 Vaticle 3 | # 4 | # Licensed to the Apache Software Foundation (ASF) under one 5 | # or more contributor license agreements. See the NOTICE file 6 | # distributed with this work for additional information 7 | # regarding copyright ownership. The ASF licenses this file 8 | # to you under the Apache License, Version 2.0 (the 9 | # "License"); you may not use this file except in compliance 10 | # with the License. You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, 15 | # software distributed under the License is distributed on an 16 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | # KIND, either express or implied. See the License for the 18 | # specific language governing permissions and limitations 19 | # under the License. 20 | # 21 | 22 | deployment = { 23 | "github.organisation": "vaticle", 24 | "github.repository": "typedb-client-rust" 25 | } 26 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2022 Vaticle 3 | # 4 | # Licensed to the Apache Software Foundation (ASF) under one 5 | # or more contributor license agreements. See the NOTICE file 6 | # distributed with this work for additional information 7 | # regarding copyright ownership. The ASF licenses this file 8 | # to you under the Apache License, Version 2.0 (the 9 | # "License"); you may not use this file except in compliance 10 | # with the License. You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, 15 | # software distributed under the License is distributed on an 16 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | # KIND, either express or implied. See the License for the 18 | # specific language governing permissions and limitations 19 | # under the License. 20 | # 21 | # 22 | 23 | imports_granularity = "Crate" 24 | group_imports = "StdExternalCrate" 25 | use_small_heuristics = "Max" 26 | max_width = 120 27 | -------------------------------------------------------------------------------- /src/answer/concept_map.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::{ 23 | collections::{hash_map, HashMap}, 24 | ops::Index, 25 | }; 26 | 27 | use crate::concept::Concept; 28 | 29 | #[derive(Debug)] 30 | pub struct ConceptMap { 31 | pub map: HashMap, 32 | } 33 | 34 | impl ConceptMap { 35 | pub fn get(&self, var_name: &str) -> Option<&Concept> { 36 | self.map.get(var_name) 37 | } 38 | 39 | pub fn concepts(&self) -> impl Iterator { 40 | self.map.values() 41 | } 42 | 43 | pub fn concepts_to_vec(&self) -> Vec<&Concept> { 44 | self.concepts().collect::>() 45 | } 46 | } 47 | 48 | impl Clone for ConceptMap { 49 | fn clone(&self) -> Self { 50 | let mut map = HashMap::with_capacity(self.map.len()); 51 | for (k, v) in &self.map { 52 | map.insert(k.clone(), v.clone()); 53 | } 54 | Self { map } 55 | } 56 | } 57 | 58 | impl From for HashMap { 59 | fn from(cm: ConceptMap) -> Self { 60 | cm.map 61 | } 62 | } 63 | 64 | impl Index for ConceptMap { 65 | type Output = Concept; 66 | 67 | fn index(&self, index: String) -> &Self::Output { 68 | &self.map[&index] 69 | } 70 | } 71 | 72 | impl IntoIterator for ConceptMap { 73 | type Item = (String, Concept); 74 | type IntoIter = hash_map::IntoIter; 75 | 76 | fn into_iter(self) -> Self::IntoIter { 77 | self.map.into_iter() 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/answer/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | mod concept_map; 23 | mod numeric; 24 | 25 | pub use self::{concept_map::ConceptMap, numeric::Numeric}; 26 | -------------------------------------------------------------------------------- /src/answer/numeric.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | #[derive(Clone, Debug)] 23 | pub enum Numeric { 24 | Long(i64), 25 | Double(f64), 26 | NaN, 27 | } 28 | 29 | impl Numeric { 30 | pub fn into_i64(self) -> i64 { 31 | if let Self::Long(value) = self { 32 | value 33 | } else { 34 | panic!() 35 | } 36 | } 37 | 38 | pub fn into_f64(self) -> f64 { 39 | if let Self::Double(value) = self { 40 | value 41 | } else { 42 | panic!() 43 | } 44 | } 45 | } 46 | 47 | impl From for i64 { 48 | fn from(n: Numeric) -> Self { 49 | n.into_i64() 50 | } 51 | } 52 | 53 | impl From for f64 { 54 | fn from(n: Numeric) -> Self { 55 | n.into_f64() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/common/address.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::{fmt, str::FromStr}; 23 | 24 | use http::Uri; 25 | 26 | use crate::common::{Error, Result}; 27 | 28 | #[derive(Clone, Debug, Hash, PartialEq, Eq)] 29 | pub(crate) struct Address { 30 | uri: Uri, 31 | } 32 | 33 | impl Address { 34 | pub fn into_uri(self) -> Uri { 35 | self.uri 36 | } 37 | } 38 | 39 | impl FromStr for Address { 40 | type Err = Error; 41 | 42 | fn from_str(address: &str) -> Result { 43 | let uri = if address.contains("://") { 44 | address.parse::()? 45 | } else { 46 | format!("http://{address}").parse::()? 47 | }; 48 | Ok(Self { uri }) 49 | } 50 | } 51 | 52 | impl fmt::Display for Address { 53 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 54 | write!(f, "{}", self.uri) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/common/credential.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::{fmt, fs, path::Path}; 23 | 24 | use tonic::transport::{Certificate, ClientTlsConfig}; 25 | 26 | use crate::Result; 27 | 28 | #[derive(Clone)] 29 | pub struct Credential { 30 | username: String, 31 | password: String, 32 | is_tls_enabled: bool, 33 | tls_config: Option, 34 | } 35 | 36 | impl Credential { 37 | pub fn with_tls(username: &str, password: &str, tls_root_ca: Option<&Path>) -> Result { 38 | let tls_config = Some(if let Some(tls_root_ca) = tls_root_ca { 39 | ClientTlsConfig::new().ca_certificate(Certificate::from_pem(fs::read_to_string(tls_root_ca)?)) 40 | } else { 41 | ClientTlsConfig::new() 42 | }); 43 | 44 | Ok(Credential { 45 | username: username.to_owned(), 46 | password: password.to_owned(), 47 | is_tls_enabled: true, 48 | tls_config, 49 | }) 50 | } 51 | 52 | pub fn without_tls(username: &str, password: &str) -> Self { 53 | Credential { 54 | username: username.to_owned(), 55 | password: password.to_owned(), 56 | is_tls_enabled: false, 57 | tls_config: None, 58 | } 59 | } 60 | 61 | pub fn username(&self) -> &str { 62 | &self.username 63 | } 64 | 65 | pub fn password(&self) -> &str { 66 | &self.password 67 | } 68 | 69 | pub fn is_tls_enabled(&self) -> bool { 70 | self.is_tls_enabled 71 | } 72 | 73 | pub fn tls_config(&self) -> &Option { 74 | &self.tls_config 75 | } 76 | } 77 | 78 | impl fmt::Debug for Credential { 79 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 80 | f.debug_struct("Credential") 81 | .field("username", &self.username) 82 | .field("is_tls_enabled", &self.is_tls_enabled) 83 | .field("tls_config", &self.tls_config) 84 | .finish() 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/common/error.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::{error::Error as StdError, fmt}; 23 | 24 | use tonic::{Code, Status}; 25 | use typeql_lang::error_messages; 26 | 27 | use crate::common::RequestID; 28 | 29 | error_messages! { ConnectionError 30 | code: "CXN", type: "Connection Error", 31 | ConnectionIsClosed() = 32 | 1: "The connection has been closed and no further operation is allowed.", 33 | SessionIsClosed() = 34 | 2: "The session is closed and no further operation is allowed.", 35 | TransactionIsClosed() = 36 | 3: "The transaction is closed and no further operation is allowed.", 37 | TransactionIsClosedWithErrors(String) = 38 | 4: "The transaction is closed because of the error(s):\n{}", 39 | UnableToConnect() = 40 | 5: "Unable to connect to TypeDB server.", 41 | DatabaseDoesNotExist(String) = 42 | 8: "The database '{}' does not exist.", 43 | MissingResponseField(&'static str) = 44 | 9: "Missing field in message received from server: '{}'.", 45 | UnknownRequestId(RequestID) = 46 | 10: "Received a response with unknown request id '{}'", 47 | ClusterUnableToConnect(String) = 48 | 12: "Unable to connect to TypeDB Cluster. Attempted connecting to the cluster members, but none are available: '{}'.", 49 | ClusterReplicaNotPrimary() = 50 | 13: "The replica is not the primary replica.", 51 | ClusterAllNodesFailed(String) = 52 | 14: "Attempted connecting to all cluster members, but the following errors occurred: \n{}.", 53 | ClusterTokenCredentialInvalid() = 54 | 16: "Invalid token credential.", 55 | SessionCloseFailed() = 56 | 17: "Failed to close session. It may still be open on the server: or it may already have been closed previously.", 57 | } 58 | 59 | error_messages! { InternalError 60 | code: "INT", type: "Internal Error", 61 | RecvError() = 62 | 1: "Channel is closed.", 63 | SendError() = 64 | 2: "Channel is closed.", 65 | UnexpectedRequestType(String) = 66 | 3: "Unexpected request type for remote procedure call: {}.", 67 | UnexpectedResponseType(String) = 68 | 4: "Unexpected response type for remote procedure call: {}.", 69 | UnknownConnectionAddress(String) = 70 | 5: "Received unrecognized address from the server: {}.", 71 | EnumOutOfBounds(i32, &'static str) = 72 | 6: "Value '{}' is out of bounds for enum '{}'.", 73 | } 74 | 75 | #[derive(Clone, Debug, PartialEq, Eq)] 76 | pub enum Error { 77 | Connection(ConnectionError), 78 | Internal(InternalError), 79 | Other(String), 80 | } 81 | 82 | impl fmt::Display for Error { 83 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 84 | match self { 85 | Error::Connection(error) => write!(f, "{error}"), 86 | Error::Internal(error) => write!(f, "{error}"), 87 | Error::Other(message) => write!(f, "{message}"), 88 | } 89 | } 90 | } 91 | 92 | impl StdError for Error { 93 | fn source(&self) -> Option<&(dyn StdError + 'static)> { 94 | match self { 95 | Error::Connection(error) => Some(error), 96 | Error::Internal(error) => Some(error), 97 | Error::Other(_) => None, 98 | } 99 | } 100 | } 101 | 102 | impl From for Error { 103 | fn from(error: ConnectionError) -> Self { 104 | Error::Connection(error) 105 | } 106 | } 107 | 108 | impl From for Error { 109 | fn from(error: InternalError) -> Self { 110 | Error::Internal(error) 111 | } 112 | } 113 | 114 | impl From for Error { 115 | fn from(status: Status) -> Self { 116 | if is_rst_stream(&status) { 117 | Self::Connection(ConnectionError::UnableToConnect()) 118 | } else if is_replica_not_primary(&status) { 119 | Self::Connection(ConnectionError::ClusterReplicaNotPrimary()) 120 | } else if is_token_credential_invalid(&status) { 121 | Self::Connection(ConnectionError::ClusterTokenCredentialInvalid()) 122 | } else { 123 | Self::Other(status.message().to_string()) 124 | } 125 | } 126 | } 127 | 128 | fn is_rst_stream(status: &Status) -> bool { 129 | // "Received Rst Stream" occurs if the server is in the process of shutting down. 130 | status.code() == Code::Unavailable 131 | || status.code() == Code::Unknown 132 | || status.message().contains("Received Rst Stream") 133 | } 134 | 135 | fn is_replica_not_primary(status: &Status) -> bool { 136 | status.code() == Code::Internal && status.message().contains("[RPL01]") 137 | } 138 | 139 | fn is_token_credential_invalid(status: &Status) -> bool { 140 | status.code() == Code::Unauthenticated && status.message().contains("[CLS08]") 141 | } 142 | 143 | impl From for Error { 144 | fn from(err: http::uri::InvalidUri) -> Self { 145 | Error::Other(err.to_string()) 146 | } 147 | } 148 | 149 | impl From for Error { 150 | fn from(err: tonic::transport::Error) -> Self { 151 | Error::Other(err.to_string()) 152 | } 153 | } 154 | 155 | impl From> for Error { 156 | fn from(err: tokio::sync::mpsc::error::SendError) -> Self { 157 | Error::Other(err.to_string()) 158 | } 159 | } 160 | 161 | impl From for Error { 162 | fn from(_err: tokio::sync::oneshot::error::RecvError) -> Self { 163 | Error::Internal(InternalError::RecvError()) 164 | } 165 | } 166 | 167 | impl From for Error { 168 | fn from(_err: crossbeam::channel::RecvError) -> Self { 169 | Error::Internal(InternalError::RecvError()) 170 | } 171 | } 172 | 173 | impl From> for Error { 174 | fn from(_err: crossbeam::channel::SendError) -> Self { 175 | Error::Internal(InternalError::SendError()) 176 | } 177 | } 178 | 179 | impl From for Error { 180 | fn from(err: String) -> Self { 181 | Error::Other(err) 182 | } 183 | } 184 | 185 | impl From for Error { 186 | fn from(err: std::io::Error) -> Self { 187 | Error::Other(err.to_string()) 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/common/id.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::fmt; 23 | 24 | use uuid::Uuid; 25 | 26 | #[derive(Clone, Eq, Hash, PartialEq)] 27 | pub struct ID(Vec); 28 | 29 | impl ID { 30 | pub(crate) fn generate() -> Self { 31 | Uuid::new_v4().as_bytes().to_vec().into() 32 | } 33 | } 34 | 35 | impl From for Vec { 36 | fn from(id: ID) -> Self { 37 | id.0 38 | } 39 | } 40 | 41 | impl From> for ID { 42 | fn from(vec: Vec) -> Self { 43 | Self(vec) 44 | } 45 | } 46 | 47 | impl fmt::Debug for ID { 48 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 49 | write!(f, "ID[{self}]") 50 | } 51 | } 52 | 53 | impl fmt::Display for ID { 54 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 55 | self.0.iter().try_for_each(|byte| write!(f, "{byte:02x}")) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/common/info.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::time::Duration; 23 | 24 | use super::{address::Address, SessionID}; 25 | 26 | #[derive(Clone, Debug)] 27 | pub(crate) struct SessionInfo { 28 | pub(crate) address: Address, 29 | pub(crate) session_id: SessionID, 30 | pub(crate) network_latency: Duration, 31 | } 32 | 33 | #[derive(Debug)] 34 | pub(crate) struct DatabaseInfo { 35 | pub(crate) name: String, 36 | pub(crate) replicas: Vec, 37 | } 38 | 39 | #[derive(Debug)] 40 | pub(crate) struct ReplicaInfo { 41 | pub(crate) address: Address, 42 | pub(crate) is_primary: bool, 43 | pub(crate) is_preferred: bool, 44 | pub(crate) term: i64, 45 | } 46 | -------------------------------------------------------------------------------- /src/common/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | pub(crate) mod address; 23 | mod credential; 24 | pub mod error; 25 | mod id; 26 | pub(crate) mod info; 27 | mod options; 28 | 29 | pub use self::{credential::Credential, error::Error, options::Options}; 30 | 31 | pub(crate) type StdResult = std::result::Result; 32 | pub type Result = StdResult; 33 | 34 | pub(crate) type RequestID = id::ID; 35 | pub(crate) type SessionID = id::ID; 36 | 37 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 38 | pub enum SessionType { 39 | Data = 0, 40 | Schema = 1, 41 | } 42 | 43 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 44 | pub enum TransactionType { 45 | Read = 0, 46 | Write = 1, 47 | } 48 | -------------------------------------------------------------------------------- /src/common/options.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::time::Duration; 23 | 24 | #[derive(Clone, Debug, Default)] 25 | pub struct Options { 26 | pub infer: Option, 27 | pub trace_inference: Option, 28 | pub explain: Option, 29 | pub parallel: Option, 30 | pub prefetch: Option, 31 | pub prefetch_size: Option, 32 | pub session_idle_timeout: Option, 33 | pub transaction_timeout: Option, 34 | pub schema_lock_acquire_timeout: Option, 35 | pub read_any_replica: Option, 36 | } 37 | 38 | impl Options { 39 | pub fn new() -> Self { 40 | Self::default() 41 | } 42 | 43 | pub fn infer(self, infer: bool) -> Self { 44 | Self { infer: Some(infer), ..self } 45 | } 46 | 47 | pub fn trace_inference(self, trace_inference: bool) -> Self { 48 | Self { trace_inference: Some(trace_inference), ..self } 49 | } 50 | 51 | pub fn explain(self, explain: bool) -> Self { 52 | Self { explain: Some(explain), ..self } 53 | } 54 | 55 | pub fn parallel(self, parallel: bool) -> Self { 56 | Self { parallel: Some(parallel), ..self } 57 | } 58 | 59 | pub fn prefetch(self, prefetch: bool) -> Self { 60 | Self { prefetch: Some(prefetch), ..self } 61 | } 62 | 63 | pub fn prefetch_size(self, prefetch_size: i32) -> Self { 64 | Self { prefetch_size: Some(prefetch_size), ..self } 65 | } 66 | 67 | pub fn session_idle_timeout(self, timeout: Duration) -> Self { 68 | Self { session_idle_timeout: Some(timeout), ..self } 69 | } 70 | 71 | pub fn transaction_timeout(self, timeout: Duration) -> Self { 72 | Self { transaction_timeout: Some(timeout), ..self } 73 | } 74 | 75 | pub fn schema_lock_acquire_timeout(self, timeout: Duration) -> Self { 76 | Self { schema_lock_acquire_timeout: Some(timeout), ..self } 77 | } 78 | 79 | pub fn read_any_replica(self, read_any_replica: bool) -> Self { 80 | Self { read_any_replica: Some(read_any_replica), ..self } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/concept/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | #![allow(dead_code)] 23 | #![allow(unused)] 24 | 25 | use std::{ 26 | convert::TryFrom, 27 | fmt, 28 | fmt::{Debug, Display, Formatter}, 29 | }; 30 | 31 | use chrono::NaiveDateTime; 32 | use futures::{FutureExt, Stream, StreamExt}; 33 | 34 | use crate::common::{error::ConnectionError, Result}; 35 | 36 | #[derive(Clone, Debug)] 37 | pub enum Concept { 38 | Type(Type), 39 | Thing(Thing), 40 | } 41 | 42 | #[derive(Clone, Debug)] 43 | pub enum Type { 44 | Thing(ThingType), 45 | Role(RoleType), 46 | } 47 | 48 | #[derive(Clone, Debug)] 49 | pub enum ThingType { 50 | Root(RootThingType), 51 | Entity(EntityType), 52 | Relation(RelationType), 53 | Attribute(AttributeType), 54 | } 55 | 56 | #[derive(Debug)] 57 | pub enum EntityOrThingType { 58 | EntityType(EntityType), 59 | RootThingType(RootThingType), 60 | } 61 | 62 | #[derive(Clone, Debug)] 63 | pub struct RootThingType { 64 | pub label: String, 65 | } 66 | 67 | impl RootThingType { 68 | const LABEL: &'static str = "thing"; 69 | 70 | pub fn new() -> Self { 71 | Self { label: String::from(Self::LABEL) } 72 | } 73 | } 74 | 75 | impl Default for RootThingType { 76 | fn default() -> Self { 77 | Self::new() 78 | } 79 | } 80 | 81 | #[derive(Clone, Debug)] 82 | pub struct EntityType { 83 | pub label: String, 84 | } 85 | 86 | impl EntityType { 87 | pub fn new(label: String) -> Self { 88 | Self { label } 89 | } 90 | } 91 | 92 | #[derive(Clone, Debug)] 93 | pub struct RelationType { 94 | pub label: String, 95 | } 96 | 97 | impl RelationType { 98 | pub fn new(label: String) -> Self { 99 | Self { label } 100 | } 101 | } 102 | 103 | #[derive(Clone, Debug)] 104 | pub enum AttributeType { 105 | Root(RootAttributeType), 106 | Boolean(BooleanAttributeType), 107 | Long(LongAttributeType), 108 | Double(DoubleAttributeType), 109 | String(StringAttributeType), 110 | DateTime(DateTimeAttributeType), 111 | } 112 | 113 | #[derive(Clone, Debug)] 114 | pub struct RootAttributeType { 115 | pub label: String, 116 | } 117 | 118 | impl RootAttributeType { 119 | const LABEL: &'static str = "attribute"; 120 | 121 | pub fn new() -> Self { 122 | Self { label: String::from(Self::LABEL) } 123 | } 124 | } 125 | 126 | impl Default for RootAttributeType { 127 | fn default() -> Self { 128 | Self::new() 129 | } 130 | } 131 | 132 | #[derive(Clone, Debug)] 133 | pub struct BooleanAttributeType { 134 | pub label: String, 135 | } 136 | 137 | impl BooleanAttributeType { 138 | pub fn new(label: String) -> Self { 139 | Self { label } 140 | } 141 | } 142 | 143 | #[derive(Clone, Debug)] 144 | pub struct LongAttributeType { 145 | pub label: String, 146 | } 147 | 148 | impl LongAttributeType { 149 | pub fn new(label: String) -> Self { 150 | Self { label } 151 | } 152 | } 153 | 154 | #[derive(Clone, Debug)] 155 | pub struct DoubleAttributeType { 156 | pub label: String, 157 | } 158 | 159 | impl DoubleAttributeType { 160 | pub fn new(label: String) -> Self { 161 | Self { label } 162 | } 163 | } 164 | 165 | #[derive(Clone, Debug)] 166 | pub struct StringAttributeType { 167 | pub label: String, 168 | } 169 | 170 | impl StringAttributeType { 171 | pub fn new(label: String) -> Self { 172 | Self { label } 173 | } 174 | } 175 | 176 | #[derive(Clone, Debug)] 177 | pub struct DateTimeAttributeType { 178 | pub label: String, 179 | } 180 | 181 | impl DateTimeAttributeType { 182 | pub fn new(label: String) -> Self { 183 | Self { label } 184 | } 185 | } 186 | 187 | #[derive(Clone, Debug)] 188 | pub struct RoleType { 189 | pub label: ScopedLabel, 190 | } 191 | 192 | impl RoleType { 193 | pub fn new(label: ScopedLabel) -> Self { 194 | Self { label } 195 | } 196 | } 197 | 198 | #[derive(Clone, Debug)] 199 | // #[enum_dispatch(ThingApi)] 200 | pub enum Thing { 201 | Entity(Entity), 202 | Relation(Relation), 203 | Attribute(Attribute), 204 | } 205 | 206 | // impl ConceptApi for Thing {} 207 | 208 | // impl ThingApi for Thing { 209 | // fn get_iid(&self) -> &Vec { 210 | // match self { 211 | // Thing::Entity(x) => { x.get_iid() } 212 | // Thing::Relation(x) => { x.get_iid() } 213 | // Thing::Attribute(x) => { x.get_iid() } 214 | // } 215 | // } 216 | // } 217 | 218 | // TODO: Storing the Type here is *extremely* inefficient; we could be effectively creating 219 | // 1 million copies of the same data when matching concepts of homogeneous types 220 | #[derive(Clone, Debug)] 221 | pub struct Entity { 222 | pub iid: Vec, 223 | pub type_: EntityType, 224 | } 225 | 226 | // impl ThingApi for Entity { 227 | // // TODO: use enum_dispatch macro to avoid manually writing the duplicates of this method 228 | // fn get_iid(&self) -> &Vec { 229 | // &self.iid 230 | // } 231 | // } 232 | 233 | // impl ConceptApi for Entity {} 234 | 235 | // impl EntityApi for Entity {} 236 | 237 | #[derive(Clone, Debug)] 238 | pub struct Relation { 239 | pub iid: Vec, 240 | pub type_: RelationType, 241 | } 242 | 243 | // macro_rules! default_impl { 244 | // { impl $trait:ident $body:tt for $($t:ident),* $(,)? } => { 245 | // $(impl $trait for $t $body)* 246 | // } 247 | // } 248 | // 249 | // default_impl! { 250 | // impl ThingApi { 251 | // fn get_iid(&self) -> &Vec { 252 | // &self.iid 253 | // } 254 | // } for Entity, Relation 255 | // } 256 | 257 | // impl ConceptApi for Relation {} 258 | 259 | // impl RelationApi for Relation {} 260 | 261 | #[derive(Clone, Debug)] 262 | pub enum Attribute { 263 | Boolean(BooleanAttribute), 264 | Long(LongAttribute), 265 | Double(DoubleAttribute), 266 | String(StringAttribute), 267 | DateTime(DateTimeAttribute), 268 | } 269 | 270 | #[derive(Clone, Debug)] 271 | pub struct BooleanAttribute { 272 | pub iid: Vec, 273 | pub value: bool, 274 | } 275 | 276 | #[derive(Clone, Debug)] 277 | pub struct LongAttribute { 278 | pub iid: Vec, 279 | pub value: i64, 280 | } 281 | 282 | impl LongAttribute {} 283 | 284 | #[derive(Clone, Debug)] 285 | pub struct DoubleAttribute { 286 | pub iid: Vec, 287 | pub value: f64, 288 | } 289 | 290 | #[derive(Clone, Debug)] 291 | pub struct StringAttribute { 292 | pub iid: Vec, 293 | pub value: String, 294 | } 295 | 296 | impl StringAttribute {} 297 | 298 | #[derive(Clone, Debug)] 299 | pub struct DateTimeAttribute { 300 | pub iid: Vec, 301 | pub value: NaiveDateTime, 302 | } 303 | 304 | pub mod attribute { 305 | #[derive(Copy, Clone, Debug)] 306 | pub enum ValueType { 307 | Object = 0, 308 | Boolean = 1, 309 | Long = 2, 310 | Double = 3, 311 | String = 4, 312 | DateTime = 5, 313 | } 314 | } 315 | 316 | #[derive(Clone, Debug)] 317 | pub struct ScopedLabel { 318 | pub scope: String, 319 | pub name: String, 320 | } 321 | 322 | impl ScopedLabel { 323 | pub fn new(scope: String, name: String) -> Self { 324 | Self { scope, name } 325 | } 326 | } 327 | 328 | impl fmt::Display for ScopedLabel { 329 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 330 | write!(f, "{}:{}", self.scope, self.name) 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /src/connection/message.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::time::Duration; 23 | 24 | use tokio::sync::mpsc::UnboundedSender; 25 | use tonic::Streaming; 26 | use typedb_protocol::transaction; 27 | 28 | use crate::{ 29 | answer::{ConceptMap, Numeric}, 30 | common::{address::Address, info::DatabaseInfo, RequestID, SessionID}, 31 | Options, SessionType, TransactionType, 32 | }; 33 | 34 | #[derive(Debug)] 35 | pub(super) enum Request { 36 | ServersAll, 37 | 38 | DatabasesContains { database_name: String }, 39 | DatabaseCreate { database_name: String }, 40 | DatabaseGet { database_name: String }, 41 | DatabasesAll, 42 | 43 | DatabaseSchema { database_name: String }, 44 | DatabaseTypeSchema { database_name: String }, 45 | DatabaseRuleSchema { database_name: String }, 46 | DatabaseDelete { database_name: String }, 47 | 48 | SessionOpen { database_name: String, session_type: SessionType, options: Options }, 49 | SessionClose { session_id: SessionID }, 50 | SessionPulse { session_id: SessionID }, 51 | 52 | Transaction(TransactionRequest), 53 | } 54 | 55 | #[derive(Debug)] 56 | pub(super) enum Response { 57 | ServersAll { 58 | servers: Vec
, 59 | }, 60 | 61 | DatabasesContains { 62 | contains: bool, 63 | }, 64 | DatabaseCreate, 65 | DatabaseGet { 66 | database: DatabaseInfo, 67 | }, 68 | DatabasesAll { 69 | databases: Vec, 70 | }, 71 | 72 | DatabaseDelete, 73 | DatabaseSchema { 74 | schema: String, 75 | }, 76 | DatabaseTypeSchema { 77 | schema: String, 78 | }, 79 | DatabaseRuleSchema { 80 | schema: String, 81 | }, 82 | 83 | SessionOpen { 84 | session_id: SessionID, 85 | server_duration: Duration, 86 | }, 87 | SessionPulse, 88 | SessionClose, 89 | 90 | TransactionOpen { 91 | request_sink: UnboundedSender, 92 | response_source: Streaming, 93 | }, 94 | } 95 | 96 | #[derive(Debug)] 97 | pub(super) enum TransactionRequest { 98 | Open { session_id: SessionID, transaction_type: TransactionType, options: Options, network_latency: Duration }, 99 | Commit, 100 | Rollback, 101 | Query(QueryRequest), 102 | Stream { request_id: RequestID }, 103 | } 104 | 105 | #[derive(Debug)] 106 | pub(super) enum TransactionResponse { 107 | Open, 108 | Commit, 109 | Rollback, 110 | Query(QueryResponse), 111 | } 112 | 113 | #[derive(Debug)] 114 | pub(super) enum QueryRequest { 115 | Define { query: String, options: Options }, 116 | Undefine { query: String, options: Options }, 117 | Delete { query: String, options: Options }, 118 | 119 | Match { query: String, options: Options }, 120 | Insert { query: String, options: Options }, 121 | Update { query: String, options: Options }, 122 | 123 | MatchAggregate { query: String, options: Options }, 124 | 125 | Explain { explainable_id: i64, options: Options }, // TODO: ID type 126 | 127 | MatchGroup { query: String, options: Options }, 128 | MatchGroupAggregate { query: String, options: Options }, 129 | } 130 | 131 | #[derive(Debug)] 132 | pub(super) enum QueryResponse { 133 | Define, 134 | Undefine, 135 | Delete, 136 | 137 | Match { answers: Vec }, 138 | Insert { answers: Vec }, 139 | Update { answers: Vec }, 140 | 141 | MatchAggregate { answer: Numeric }, 142 | 143 | Explain {}, // TODO: explanations 144 | 145 | MatchGroup {}, // TODO: ConceptMapGroup 146 | MatchGroupAggregate {}, // TODO: NumericGroup 147 | } 148 | -------------------------------------------------------------------------------- /src/connection/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | mod connection; 23 | mod message; 24 | mod network; 25 | mod runtime; 26 | mod transaction_stream; 27 | 28 | pub use self::connection::Connection; 29 | pub(crate) use self::{connection::ServerConnection, transaction_stream::TransactionStream}; 30 | -------------------------------------------------------------------------------- /src/connection/network/channel.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::sync::{Arc, RwLock}; 23 | 24 | use tonic::{ 25 | body::BoxBody, 26 | client::GrpcService, 27 | service::{ 28 | interceptor::{self, InterceptedService}, 29 | Interceptor, 30 | }, 31 | transport::{channel, Channel, Error as TonicError}, 32 | Request, Status, 33 | }; 34 | 35 | use crate::{ 36 | common::{address::Address, Result, StdResult}, 37 | Credential, 38 | }; 39 | 40 | type ResponseFuture = interceptor::ResponseFuture; 41 | 42 | pub(super) type PlainTextChannel = InterceptedService; 43 | pub(super) type CallCredChannel = InterceptedService; 44 | 45 | pub(super) trait GRPCChannel: 46 | GrpcService + Clone + Send + 'static 47 | { 48 | fn is_plaintext(&self) -> bool; 49 | } 50 | 51 | impl GRPCChannel for PlainTextChannel { 52 | fn is_plaintext(&self) -> bool { 53 | true 54 | } 55 | } 56 | 57 | impl GRPCChannel for CallCredChannel { 58 | fn is_plaintext(&self) -> bool { 59 | false 60 | } 61 | } 62 | 63 | pub(super) fn open_plaintext_channel(address: Address) -> PlainTextChannel { 64 | PlainTextChannel::new(Channel::builder(address.into_uri()).connect_lazy(), PlainTextFacade) 65 | } 66 | 67 | #[derive(Clone, Debug)] 68 | pub(super) struct PlainTextFacade; 69 | 70 | impl Interceptor for PlainTextFacade { 71 | fn call(&mut self, request: Request<()>) -> StdResult, Status> { 72 | Ok(request) 73 | } 74 | } 75 | 76 | pub(super) fn open_encrypted_channel( 77 | address: Address, 78 | credential: Credential, 79 | ) -> Result<(CallCredChannel, Arc)> { 80 | let mut builder = Channel::builder(address.into_uri()); 81 | if credential.is_tls_enabled() { 82 | builder = builder.tls_config(credential.tls_config().clone().unwrap())?; 83 | } 84 | let channel = builder.connect_lazy(); 85 | let call_credentials = Arc::new(CallCredentials::new(credential)); 86 | Ok((CallCredChannel::new(channel, CredentialInjector::new(call_credentials.clone())), call_credentials)) 87 | } 88 | 89 | #[derive(Debug)] 90 | pub(super) struct CallCredentials { 91 | credential: Credential, 92 | token: RwLock>, 93 | } 94 | 95 | impl CallCredentials { 96 | pub(super) fn new(credential: Credential) -> Self { 97 | Self { credential, token: RwLock::new(None) } 98 | } 99 | 100 | pub(super) fn username(&self) -> &str { 101 | self.credential.username() 102 | } 103 | 104 | pub(super) fn set_token(&self, token: String) { 105 | *self.token.write().unwrap() = Some(token); 106 | } 107 | 108 | pub(super) fn reset_token(&self) { 109 | *self.token.write().unwrap() = None; 110 | } 111 | 112 | pub(super) fn inject(&self, mut request: Request<()>) -> Request<()> { 113 | request.metadata_mut().insert("username", self.credential.username().try_into().unwrap()); 114 | match &*self.token.read().unwrap() { 115 | Some(token) => request.metadata_mut().insert("token", token.try_into().unwrap()), 116 | None => request.metadata_mut().insert("password", self.credential.password().try_into().unwrap()), 117 | }; 118 | request 119 | } 120 | } 121 | 122 | #[derive(Clone, Debug)] 123 | pub(super) struct CredentialInjector { 124 | call_credentials: Arc, 125 | } 126 | 127 | impl CredentialInjector { 128 | pub(super) fn new(call_credentials: Arc) -> Self { 129 | Self { call_credentials } 130 | } 131 | } 132 | 133 | impl Interceptor for CredentialInjector { 134 | fn call(&mut self, request: Request<()>) -> StdResult, Status> { 135 | Ok(self.call_credentials.inject(request)) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/connection/network/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | mod channel; 23 | mod proto; 24 | mod stub; 25 | pub(super) mod transmitter; 26 | -------------------------------------------------------------------------------- /src/connection/network/proto/common.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use typedb_protocol::{ 23 | options::{ 24 | ExplainOpt::Explain, InferOpt::Infer, ParallelOpt::Parallel, PrefetchOpt::Prefetch, 25 | PrefetchSizeOpt::PrefetchSize, ReadAnyReplicaOpt::ReadAnyReplica, 26 | SchemaLockAcquireTimeoutOpt::SchemaLockAcquireTimeoutMillis, SessionIdleTimeoutOpt::SessionIdleTimeoutMillis, 27 | TraceInferenceOpt::TraceInference, TransactionTimeoutOpt::TransactionTimeoutMillis, 28 | }, 29 | session, transaction, Options as OptionsProto, 30 | }; 31 | 32 | use super::IntoProto; 33 | use crate::{Options, SessionType, TransactionType}; 34 | 35 | impl IntoProto for SessionType { 36 | fn into_proto(self) -> session::Type { 37 | match self { 38 | SessionType::Data => session::Type::Data, 39 | SessionType::Schema => session::Type::Schema, 40 | } 41 | } 42 | } 43 | 44 | impl IntoProto for TransactionType { 45 | fn into_proto(self) -> transaction::Type { 46 | match self { 47 | TransactionType::Read => transaction::Type::Read, 48 | TransactionType::Write => transaction::Type::Write, 49 | } 50 | } 51 | } 52 | 53 | impl IntoProto for Options { 54 | fn into_proto(self) -> OptionsProto { 55 | OptionsProto { 56 | infer_opt: self.infer.map(Infer), 57 | trace_inference_opt: self.trace_inference.map(TraceInference), 58 | explain_opt: self.explain.map(Explain), 59 | parallel_opt: self.parallel.map(Parallel), 60 | prefetch_size_opt: self.prefetch_size.map(PrefetchSize), 61 | prefetch_opt: self.prefetch.map(Prefetch), 62 | session_idle_timeout_opt: self 63 | .session_idle_timeout 64 | .map(|val| SessionIdleTimeoutMillis(val.as_millis() as i32)), 65 | transaction_timeout_opt: self 66 | .transaction_timeout 67 | .map(|val| TransactionTimeoutMillis(val.as_millis() as i32)), 68 | schema_lock_acquire_timeout_opt: self 69 | .schema_lock_acquire_timeout 70 | .map(|val| SchemaLockAcquireTimeoutMillis(val.as_millis() as i32)), 71 | read_any_replica_opt: self.read_any_replica.map(ReadAnyReplica), 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/connection/network/proto/concept.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::collections::HashMap; 23 | 24 | use chrono::NaiveDateTime; 25 | use typedb_protocol::{ 26 | attribute::value::Value as ValueProto, attribute_type::ValueType, concept as concept_proto, numeric::Value, 27 | r#type::Encoding, Concept as ConceptProto, ConceptMap as ConceptMapProto, Numeric as NumericProto, 28 | Thing as ThingProto, Type as TypeProto, 29 | }; 30 | 31 | use super::TryFromProto; 32 | use crate::{ 33 | answer::{ConceptMap, Numeric}, 34 | concept::{ 35 | Attribute, AttributeType, BooleanAttribute, BooleanAttributeType, Concept, DateTimeAttribute, 36 | DateTimeAttributeType, DoubleAttribute, DoubleAttributeType, Entity, EntityType, LongAttribute, 37 | LongAttributeType, Relation, RelationType, RoleType, RootAttributeType, RootThingType, ScopedLabel, 38 | StringAttribute, StringAttributeType, Thing, ThingType, Type, 39 | }, 40 | connection::network::proto::FromProto, 41 | error::{ConnectionError, InternalError}, 42 | Result, 43 | }; 44 | 45 | impl TryFromProto for Numeric { 46 | fn try_from_proto(proto: NumericProto) -> Result { 47 | match proto.value { 48 | Some(Value::LongValue(long)) => Ok(Numeric::Long(long)), 49 | Some(Value::DoubleValue(double)) => Ok(Numeric::Double(double)), 50 | Some(Value::Nan(_)) => Ok(Numeric::NaN), 51 | None => Err(ConnectionError::MissingResponseField("value").into()), 52 | } 53 | } 54 | } 55 | 56 | impl TryFromProto for ConceptMap { 57 | fn try_from_proto(proto: ConceptMapProto) -> Result { 58 | let mut map = HashMap::with_capacity(proto.map.len()); 59 | for (k, v) in proto.map { 60 | map.insert(k, Concept::try_from_proto(v)?); 61 | } 62 | Ok(Self { map }) 63 | } 64 | } 65 | 66 | impl TryFromProto for Concept { 67 | fn try_from_proto(proto: ConceptProto) -> Result { 68 | let concept = proto.concept.ok_or(ConnectionError::MissingResponseField("concept"))?; 69 | match concept { 70 | concept_proto::Concept::Thing(thing) => Ok(Self::Thing(Thing::try_from_proto(thing)?)), 71 | concept_proto::Concept::Type(type_) => Ok(Self::Type(Type::try_from_proto(type_)?)), 72 | } 73 | } 74 | } 75 | 76 | impl TryFromProto for Encoding { 77 | fn try_from_proto(proto: i32) -> Result { 78 | Self::from_i32(proto).ok_or(InternalError::EnumOutOfBounds(proto, "Encoding").into()) 79 | } 80 | } 81 | 82 | impl TryFromProto for Type { 83 | fn try_from_proto(proto: TypeProto) -> Result { 84 | match Encoding::try_from_proto(proto.encoding)? { 85 | Encoding::ThingType => Ok(Self::Thing(ThingType::Root(RootThingType::default()))), 86 | Encoding::EntityType => Ok(Self::Thing(ThingType::Entity(EntityType::from_proto(proto)))), 87 | Encoding::RelationType => Ok(Self::Thing(ThingType::Relation(RelationType::from_proto(proto)))), 88 | Encoding::AttributeType => Ok(Self::Thing(ThingType::Attribute(AttributeType::try_from_proto(proto)?))), 89 | Encoding::RoleType => Ok(Self::Role(RoleType::from_proto(proto))), 90 | } 91 | } 92 | } 93 | 94 | impl FromProto for EntityType { 95 | fn from_proto(proto: TypeProto) -> Self { 96 | Self::new(proto.label) 97 | } 98 | } 99 | 100 | impl FromProto for RelationType { 101 | fn from_proto(proto: TypeProto) -> Self { 102 | Self::new(proto.label) 103 | } 104 | } 105 | 106 | impl TryFromProto for ValueType { 107 | fn try_from_proto(proto: i32) -> Result { 108 | Self::from_i32(proto).ok_or(InternalError::EnumOutOfBounds(proto, "ValueType").into()) 109 | } 110 | } 111 | 112 | impl TryFromProto for AttributeType { 113 | fn try_from_proto(proto: TypeProto) -> Result { 114 | match ValueType::try_from_proto(proto.value_type)? { 115 | ValueType::Object => Ok(Self::Root(RootAttributeType::default())), 116 | ValueType::Boolean => Ok(Self::Boolean(BooleanAttributeType { label: proto.label })), 117 | ValueType::Long => Ok(Self::Long(LongAttributeType { label: proto.label })), 118 | ValueType::Double => Ok(Self::Double(DoubleAttributeType { label: proto.label })), 119 | ValueType::String => Ok(Self::String(StringAttributeType { label: proto.label })), 120 | ValueType::Datetime => Ok(Self::DateTime(DateTimeAttributeType { label: proto.label })), 121 | } 122 | } 123 | } 124 | 125 | impl FromProto for RoleType { 126 | fn from_proto(proto: TypeProto) -> Self { 127 | Self::new(ScopedLabel::new(proto.scope, proto.label)) 128 | } 129 | } 130 | 131 | impl TryFromProto for Thing { 132 | fn try_from_proto(proto: ThingProto) -> Result { 133 | let encoding = proto.r#type.clone().ok_or(ConnectionError::MissingResponseField("type"))?.encoding; 134 | match Encoding::try_from_proto(encoding)? { 135 | Encoding::EntityType => Ok(Self::Entity(Entity::try_from_proto(proto)?)), 136 | Encoding::RelationType => Ok(Self::Relation(Relation::try_from_proto(proto)?)), 137 | Encoding::AttributeType => Ok(Self::Attribute(Attribute::try_from_proto(proto)?)), 138 | _ => todo!(), 139 | } 140 | } 141 | } 142 | 143 | impl TryFromProto for Entity { 144 | fn try_from_proto(proto: ThingProto) -> Result { 145 | Ok(Self { 146 | type_: EntityType::from_proto(proto.r#type.ok_or(ConnectionError::MissingResponseField("type"))?), 147 | iid: proto.iid, 148 | }) 149 | } 150 | } 151 | 152 | impl TryFromProto for Relation { 153 | fn try_from_proto(proto: ThingProto) -> Result { 154 | Ok(Self { 155 | type_: RelationType::from_proto(proto.r#type.ok_or(ConnectionError::MissingResponseField("type"))?), 156 | iid: proto.iid, 157 | }) 158 | } 159 | } 160 | 161 | impl TryFromProto for Attribute { 162 | fn try_from_proto(proto: ThingProto) -> Result { 163 | let value = proto.value.and_then(|v| v.value).ok_or(ConnectionError::MissingResponseField("value"))?; 164 | 165 | let value_type = proto.r#type.ok_or(ConnectionError::MissingResponseField("type"))?.value_type; 166 | let iid = proto.iid; 167 | 168 | match ValueType::try_from_proto(value_type)? { 169 | ValueType::Object => todo!(), 170 | ValueType::Boolean => Ok(Self::Boolean(BooleanAttribute { 171 | value: if let ValueProto::Boolean(value) = value { value } else { unreachable!() }, 172 | iid, 173 | })), 174 | ValueType::Long => Ok(Self::Long(LongAttribute { 175 | value: if let ValueProto::Long(value) = value { value } else { unreachable!() }, 176 | iid, 177 | })), 178 | ValueType::Double => Ok(Self::Double(DoubleAttribute { 179 | value: if let ValueProto::Double(value) = value { value } else { unreachable!() }, 180 | iid, 181 | })), 182 | ValueType::String => Ok(Self::String(StringAttribute { 183 | value: if let ValueProto::String(value) = value { value } else { unreachable!() }, 184 | iid, 185 | })), 186 | ValueType::Datetime => Ok(Self::DateTime(DateTimeAttribute { 187 | value: if let ValueProto::DateTime(value) = value { 188 | NaiveDateTime::from_timestamp_opt(value / 1000, (value % 1000) as u32 * 1_000_000).unwrap() 189 | } else { 190 | unreachable!() 191 | }, 192 | iid, 193 | })), 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/connection/network/proto/database.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use itertools::Itertools; 23 | use typedb_protocol::{cluster_database::Replica as ReplicaProto, ClusterDatabase as DatabaseProto}; 24 | 25 | use super::TryFromProto; 26 | use crate::{ 27 | common::info::{DatabaseInfo, ReplicaInfo}, 28 | Result, 29 | }; 30 | 31 | impl TryFromProto for DatabaseInfo { 32 | fn try_from_proto(proto: DatabaseProto) -> Result { 33 | Ok(Self { 34 | name: proto.name, 35 | replicas: proto.replicas.into_iter().map(ReplicaInfo::try_from_proto).try_collect()?, 36 | }) 37 | } 38 | } 39 | 40 | impl TryFromProto for ReplicaInfo { 41 | fn try_from_proto(proto: ReplicaProto) -> Result { 42 | Ok(Self { 43 | address: proto.address.as_str().parse()?, 44 | is_primary: proto.primary, 45 | is_preferred: proto.preferred, 46 | term: proto.term, 47 | }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/connection/network/proto/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | mod common; 23 | mod concept; 24 | mod database; 25 | mod message; 26 | 27 | use crate::Result; 28 | 29 | pub(super) trait IntoProto { 30 | fn into_proto(self) -> Proto; 31 | } 32 | 33 | pub(super) trait TryIntoProto { 34 | fn try_into_proto(self) -> Result; 35 | } 36 | 37 | pub(super) trait FromProto { 38 | fn from_proto(proto: Proto) -> Self; 39 | } 40 | 41 | pub(super) trait TryFromProto: Sized { 42 | fn try_from_proto(proto: Proto) -> Result; 43 | } 44 | -------------------------------------------------------------------------------- /src/connection/network/stub.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::sync::Arc; 23 | 24 | use futures::{future::BoxFuture, FutureExt, TryFutureExt}; 25 | use log::{debug, trace}; 26 | use tokio::sync::mpsc::{unbounded_channel as unbounded_async, UnboundedSender}; 27 | use tokio_stream::wrappers::UnboundedReceiverStream; 28 | use tonic::{Response, Status, Streaming}; 29 | use typedb_protocol::{ 30 | cluster_database::Replica, cluster_database_manager, cluster_user, core_database, core_database_manager, 31 | server_manager, session, transaction, type_db_client::TypeDbClient as CoreGRPC, 32 | type_db_cluster_client::TypeDbClusterClient as ClusterGRPC, ClusterDatabase, 33 | }; 34 | 35 | use super::channel::{CallCredentials, GRPCChannel}; 36 | use crate::common::{address::Address, error::ConnectionError, Error, Result, StdResult}; 37 | 38 | type TonicResult = StdResult, Status>; 39 | 40 | #[derive(Clone, Debug)] 41 | pub(super) struct RPCStub { 42 | address: Address, 43 | channel: Channel, 44 | core_grpc: CoreGRPC, 45 | cluster_grpc: ClusterGRPC, 46 | call_credentials: Option>, 47 | } 48 | 49 | impl RPCStub { 50 | pub(super) async fn new( 51 | address: Address, 52 | channel: Channel, 53 | call_credentials: Option>, 54 | ) -> Result { 55 | let this = Self { 56 | address, 57 | core_grpc: CoreGRPC::new(channel.clone()), 58 | cluster_grpc: ClusterGRPC::new(channel.clone()), 59 | channel, 60 | call_credentials, 61 | }; 62 | let mut this = this.validated().await?; 63 | this.renew_token().await?; 64 | Ok(this) 65 | } 66 | 67 | pub(super) async fn validated(mut self) -> Result { 68 | self.databases_all(cluster_database_manager::all::Req {}).await?; 69 | Ok(self) 70 | } 71 | 72 | fn address(&self) -> &Address { 73 | &self.address 74 | } 75 | 76 | async fn call_with_auto_renew_token(&mut self, call: F) -> Result 77 | where 78 | for<'a> F: Fn(&'a mut Self) -> BoxFuture<'a, Result>, 79 | { 80 | match call(self).await { 81 | Err(Error::Connection(ConnectionError::ClusterTokenCredentialInvalid())) => { 82 | self.renew_token().await?; 83 | call(self).await 84 | } 85 | res => res, 86 | } 87 | } 88 | 89 | async fn renew_token(&mut self) -> Result { 90 | if let Some(call_credentials) = &self.call_credentials { 91 | trace!("renewing token..."); 92 | call_credentials.reset_token(); 93 | let req = cluster_user::token::Req { username: call_credentials.username().to_owned() }; 94 | trace!("sending token request..."); 95 | let token = self.cluster_grpc.user_token(req).await?.into_inner().token; 96 | call_credentials.set_token(token); 97 | trace!("renewed token"); 98 | } 99 | Ok(()) 100 | } 101 | 102 | pub(super) async fn servers_all(&mut self, req: server_manager::all::Req) -> Result { 103 | self.single(|this| Box::pin(this.cluster_grpc.servers_all(req.clone()))).await 104 | } 105 | 106 | pub(super) async fn databases_contains( 107 | &mut self, 108 | req: core_database_manager::contains::Req, 109 | ) -> Result { 110 | self.single(|this| Box::pin(this.core_grpc.databases_contains(req.clone()))).await 111 | } 112 | 113 | pub(super) async fn databases_create( 114 | &mut self, 115 | req: core_database_manager::create::Req, 116 | ) -> Result { 117 | self.single(|this| Box::pin(this.core_grpc.databases_create(req.clone()))).await 118 | } 119 | 120 | // FIXME: merge after protocol merge 121 | pub(super) async fn databases_get( 122 | &mut self, 123 | req: cluster_database_manager::get::Req, 124 | ) -> Result { 125 | if self.channel.is_plaintext() { 126 | self.databases_get_core(req).await 127 | } else { 128 | self.databases_get_cluster(req).await 129 | } 130 | } 131 | 132 | pub(super) async fn databases_all( 133 | &mut self, 134 | req: cluster_database_manager::all::Req, 135 | ) -> Result { 136 | if self.channel.is_plaintext() { 137 | self.databases_all_core(req).await 138 | } else { 139 | self.databases_all_cluster(req).await 140 | } 141 | } 142 | 143 | async fn databases_get_core( 144 | &mut self, 145 | req: cluster_database_manager::get::Req, 146 | ) -> Result { 147 | Ok(cluster_database_manager::get::Res { 148 | database: Some(ClusterDatabase { 149 | name: req.name, 150 | replicas: vec![Replica { 151 | address: self.address().to_string(), 152 | primary: true, 153 | preferred: true, 154 | term: 0, 155 | }], 156 | }), 157 | }) 158 | } 159 | 160 | async fn databases_all_core( 161 | &mut self, 162 | _req: cluster_database_manager::all::Req, 163 | ) -> Result { 164 | let database_names = 165 | self.single(|this| Box::pin(this.core_grpc.databases_all(core_database_manager::all::Req {}))).await?.names; 166 | Ok(cluster_database_manager::all::Res { 167 | databases: database_names 168 | .into_iter() 169 | .map(|db_name| ClusterDatabase { 170 | name: db_name, 171 | replicas: vec![Replica { 172 | address: self.address().to_string(), 173 | primary: true, 174 | preferred: true, 175 | term: 0, 176 | }], 177 | }) 178 | .collect(), 179 | }) 180 | } 181 | 182 | async fn databases_get_cluster( 183 | &mut self, 184 | req: cluster_database_manager::get::Req, 185 | ) -> Result { 186 | self.single(|this| Box::pin(this.cluster_grpc.databases_get(req.clone()))).await 187 | } 188 | 189 | async fn databases_all_cluster( 190 | &mut self, 191 | req: cluster_database_manager::all::Req, 192 | ) -> Result { 193 | self.single(|this| Box::pin(this.cluster_grpc.databases_all(req.clone()))).await 194 | } 195 | // FIXME: end FIXME 196 | 197 | pub(super) async fn database_delete( 198 | &mut self, 199 | req: core_database::delete::Req, 200 | ) -> Result { 201 | self.single(|this| Box::pin(this.core_grpc.database_delete(req.clone()))).await 202 | } 203 | 204 | pub(super) async fn database_schema( 205 | &mut self, 206 | req: core_database::schema::Req, 207 | ) -> Result { 208 | self.single(|this| Box::pin(this.core_grpc.database_schema(req.clone()))).await 209 | } 210 | 211 | pub(super) async fn database_type_schema( 212 | &mut self, 213 | req: core_database::type_schema::Req, 214 | ) -> Result { 215 | self.single(|this| Box::pin(this.core_grpc.database_type_schema(req.clone()))).await 216 | } 217 | 218 | pub(super) async fn database_rule_schema( 219 | &mut self, 220 | req: core_database::rule_schema::Req, 221 | ) -> Result { 222 | self.single(|this| Box::pin(this.core_grpc.database_rule_schema(req.clone()))).await 223 | } 224 | 225 | pub(super) async fn session_open(&mut self, req: session::open::Req) -> Result { 226 | self.single(|this| Box::pin(this.core_grpc.session_open(req.clone()))).await 227 | } 228 | 229 | pub(super) async fn session_close(&mut self, req: session::close::Req) -> Result { 230 | debug!("closing session"); 231 | self.single(|this| Box::pin(this.core_grpc.session_close(req.clone()))).await 232 | } 233 | 234 | pub(super) async fn session_pulse(&mut self, req: session::pulse::Req) -> Result { 235 | self.single(|this| Box::pin(this.core_grpc.session_pulse(req.clone()))).await 236 | } 237 | 238 | pub(super) async fn transaction( 239 | &mut self, 240 | open_req: transaction::Req, 241 | ) -> Result<(UnboundedSender, Streaming)> { 242 | self.call_with_auto_renew_token(|this| { 243 | let transaction_req = transaction::Client { reqs: vec![open_req.clone()] }; 244 | Box::pin(async { 245 | let (sender, receiver) = unbounded_async(); 246 | sender.send(transaction_req)?; 247 | this.core_grpc 248 | .transaction(UnboundedReceiverStream::new(receiver)) 249 | .map_ok(|stream| Response::new((sender, stream.into_inner()))) 250 | .map(|r| Ok(r?.into_inner())) 251 | .await 252 | }) 253 | }) 254 | .await 255 | } 256 | 257 | async fn single(&mut self, call: F) -> Result 258 | where 259 | for<'a> F: Fn(&'a mut Self) -> BoxFuture<'a, TonicResult> + Send + Sync, 260 | R: 'static, 261 | { 262 | self.call_with_auto_renew_token(|this| Box::pin(call(this).map(|r| Ok(r?.into_inner())))).await 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /src/connection/network/transmitter/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | mod response_sink; 23 | mod rpc; 24 | mod transaction; 25 | 26 | pub(in crate::connection) use self::{rpc::RPCTransmitter, transaction::TransactionTransmitter}; 27 | -------------------------------------------------------------------------------- /src/connection/network/transmitter/response_sink.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use crossbeam::channel::Sender as SyncSender; 23 | use log::error; 24 | use tokio::sync::{mpsc::UnboundedSender, oneshot::Sender as AsyncOneshotSender}; 25 | 26 | use crate::{ 27 | common::Result, 28 | error::{ConnectionError, InternalError}, 29 | Error, 30 | }; 31 | 32 | #[derive(Debug)] 33 | pub(super) enum ResponseSink { 34 | AsyncOneShot(AsyncOneshotSender>), 35 | BlockingOneShot(SyncSender>), 36 | Streamed(UnboundedSender>), 37 | } 38 | 39 | impl ResponseSink { 40 | pub(super) fn finish(self, response: Result) { 41 | let result = match self { 42 | Self::AsyncOneShot(sink) => sink.send(response).map_err(|_| InternalError::SendError().into()), 43 | Self::BlockingOneShot(sink) => sink.send(response).map_err(Error::from), 44 | Self::Streamed(sink) => sink.send(response).map_err(Error::from), 45 | }; 46 | if let Err(err) = result { 47 | error!("{}", err); 48 | } 49 | } 50 | 51 | pub(super) fn send(&self, response: Result) { 52 | let result = match self { 53 | Self::Streamed(sink) => sink.send(response).map_err(Error::from), 54 | _ => unreachable!("attempted to stream over a one-shot callback"), 55 | }; 56 | if let Err(err) = result { 57 | error!("{}", err); 58 | } 59 | } 60 | 61 | pub(super) fn error(self, error: ConnectionError) { 62 | match self { 63 | Self::AsyncOneShot(sink) => sink.send(Err(error.into())).ok(), 64 | Self::BlockingOneShot(sink) => sink.send(Err(error.into())).ok(), 65 | Self::Streamed(sink) => sink.send(Err(error.into())).ok(), 66 | }; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/connection/network/transmitter/rpc.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use crossbeam::channel::{bounded as bounded_blocking, Receiver as SyncReceiver, Sender as SyncSender}; 23 | use tokio::{ 24 | select, 25 | sync::{ 26 | mpsc::{unbounded_channel as unbounded_async, UnboundedReceiver, UnboundedSender}, 27 | oneshot::channel as oneshot_async, 28 | }, 29 | }; 30 | 31 | use super::response_sink::ResponseSink; 32 | use crate::{ 33 | common::{address::Address, Result}, 34 | connection::{ 35 | message::{Request, Response}, 36 | network::{ 37 | channel::{open_encrypted_channel, open_plaintext_channel, GRPCChannel}, 38 | proto::{FromProto, IntoProto, TryFromProto, TryIntoProto}, 39 | stub::RPCStub, 40 | }, 41 | runtime::BackgroundRuntime, 42 | }, 43 | Credential, Error, 44 | }; 45 | 46 | fn oneshot_blocking() -> (SyncSender, SyncReceiver) { 47 | bounded_blocking::(0) 48 | } 49 | 50 | pub(in crate::connection) struct RPCTransmitter { 51 | request_sink: UnboundedSender<(Request, ResponseSink)>, 52 | shutdown_sink: UnboundedSender<()>, 53 | } 54 | 55 | impl RPCTransmitter { 56 | pub(in crate::connection) fn start_plaintext(address: Address, runtime: &BackgroundRuntime) -> Result { 57 | let (request_sink, request_source) = unbounded_async(); 58 | let (shutdown_sink, shutdown_source) = unbounded_async(); 59 | runtime.run_blocking(async move { 60 | let channel = open_plaintext_channel(address.clone()); 61 | let rpc = RPCStub::new(address.clone(), channel, None).await?; 62 | tokio::spawn(Self::dispatcher_loop(rpc, request_source, shutdown_source)); 63 | Ok::<(), Error>(()) 64 | })?; 65 | Ok(Self { request_sink, shutdown_sink }) 66 | } 67 | 68 | pub(in crate::connection) fn start_encrypted( 69 | address: Address, 70 | credential: Credential, 71 | runtime: &BackgroundRuntime, 72 | ) -> Result { 73 | let (request_sink, request_source) = unbounded_async(); 74 | let (shutdown_sink, shutdown_source) = unbounded_async(); 75 | runtime.run_blocking(async move { 76 | let (channel, call_credentials) = open_encrypted_channel(address.clone(), credential)?; 77 | let rpc = RPCStub::new(address.clone(), channel, Some(call_credentials)).await?; 78 | tokio::spawn(Self::dispatcher_loop(rpc, request_source, shutdown_source)); 79 | Ok::<(), Error>(()) 80 | })?; 81 | Ok(Self { request_sink, shutdown_sink }) 82 | } 83 | 84 | pub(in crate::connection) async fn request_async(&self, request: Request) -> Result { 85 | let (response_sink, response) = oneshot_async(); 86 | self.request_sink.send((request, ResponseSink::AsyncOneShot(response_sink)))?; 87 | response.await? 88 | } 89 | 90 | pub(in crate::connection) fn request_blocking(&self, request: Request) -> Result { 91 | let (response_sink, response) = oneshot_blocking(); 92 | self.request_sink.send((request, ResponseSink::BlockingOneShot(response_sink)))?; 93 | response.recv()? 94 | } 95 | 96 | pub(in crate::connection) fn force_close(&self) -> Result { 97 | self.shutdown_sink.send(()).map_err(Into::into) 98 | } 99 | 100 | async fn dispatcher_loop( 101 | rpc: RPCStub, 102 | mut request_source: UnboundedReceiver<(Request, ResponseSink)>, 103 | mut shutdown_signal: UnboundedReceiver<()>, 104 | ) { 105 | while let Some((request, response_sink)) = select! { 106 | request = request_source.recv() => request, 107 | _ = shutdown_signal.recv() => None, 108 | } { 109 | let rpc = rpc.clone(); 110 | tokio::spawn(async move { 111 | let response = Self::send_request(rpc, request).await; 112 | response_sink.finish(response); 113 | }); 114 | } 115 | } 116 | 117 | async fn send_request(mut rpc: RPCStub, request: Request) -> Result { 118 | match request { 119 | Request::ServersAll => rpc.servers_all(request.try_into_proto()?).await.and_then(Response::try_from_proto), 120 | 121 | Request::DatabasesContains { .. } => { 122 | rpc.databases_contains(request.try_into_proto()?).await.map(Response::from_proto) 123 | } 124 | Request::DatabaseCreate { .. } => { 125 | rpc.databases_create(request.try_into_proto()?).await.map(Response::from_proto) 126 | } 127 | Request::DatabaseGet { .. } => { 128 | rpc.databases_get(request.try_into_proto()?).await.and_then(Response::try_from_proto) 129 | } 130 | Request::DatabasesAll => { 131 | rpc.databases_all(request.try_into_proto()?).await.and_then(Response::try_from_proto) 132 | } 133 | 134 | Request::DatabaseDelete { .. } => { 135 | rpc.database_delete(request.try_into_proto()?).await.map(Response::from_proto) 136 | } 137 | Request::DatabaseSchema { .. } => { 138 | rpc.database_schema(request.try_into_proto()?).await.map(Response::from_proto) 139 | } 140 | Request::DatabaseTypeSchema { .. } => { 141 | rpc.database_type_schema(request.try_into_proto()?).await.map(Response::from_proto) 142 | } 143 | Request::DatabaseRuleSchema { .. } => { 144 | rpc.database_rule_schema(request.try_into_proto()?).await.map(Response::from_proto) 145 | } 146 | 147 | Request::SessionOpen { .. } => rpc.session_open(request.try_into_proto()?).await.map(Response::from_proto), 148 | Request::SessionPulse { .. } => { 149 | rpc.session_pulse(request.try_into_proto()?).await.map(Response::from_proto) 150 | } 151 | Request::SessionClose { .. } => { 152 | rpc.session_close(request.try_into_proto()?).await.map(Response::from_proto) 153 | } 154 | 155 | Request::Transaction(transaction_request) => { 156 | let (request_sink, response_source) = rpc.transaction(transaction_request.into_proto()).await?; 157 | Ok(Response::TransactionOpen { request_sink, response_source }) 158 | } 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/connection/runtime.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::{future::Future, thread}; 23 | 24 | use crossbeam::{atomic::AtomicCell, channel::bounded as bounded_blocking}; 25 | use tokio::{ 26 | runtime, 27 | sync::mpsc::{unbounded_channel as unbounded_async, UnboundedSender}, 28 | }; 29 | 30 | use crate::common::Result; 31 | 32 | pub(super) struct BackgroundRuntime { 33 | async_runtime_handle: runtime::Handle, 34 | is_open: AtomicCell, 35 | shutdown_sink: UnboundedSender<()>, 36 | } 37 | 38 | impl BackgroundRuntime { 39 | pub(super) fn new() -> Result { 40 | let is_open = AtomicCell::new(true); 41 | let (shutdown_sink, mut shutdown_source) = unbounded_async(); 42 | let async_runtime = runtime::Builder::new_current_thread().enable_time().enable_io().build()?; 43 | let async_runtime_handle = async_runtime.handle().clone(); 44 | thread::Builder::new().name("gRPC worker".to_string()).spawn(move || { 45 | async_runtime.block_on(async move { 46 | shutdown_source.recv().await; 47 | }); 48 | })?; 49 | Ok(Self { async_runtime_handle, is_open, shutdown_sink }) 50 | } 51 | 52 | pub(super) fn is_open(&self) -> bool { 53 | self.is_open.load() 54 | } 55 | 56 | pub(super) fn force_close(&self) -> Result { 57 | self.is_open.store(false); 58 | self.shutdown_sink.send(())?; 59 | Ok(()) 60 | } 61 | 62 | pub(super) fn spawn(&self, future: F) 63 | where 64 | F: Future + Send + 'static, 65 | F::Output: Send + 'static, 66 | { 67 | self.async_runtime_handle.spawn(future); 68 | } 69 | 70 | pub(super) fn run_blocking(&self, future: F) -> F::Output 71 | where 72 | F: Future + Send + 'static, 73 | F::Output: Send + 'static, 74 | { 75 | let (response_sink, response) = bounded_blocking(0); 76 | self.async_runtime_handle.spawn(async move { 77 | response_sink.send(future.await).ok(); 78 | }); 79 | response.recv().unwrap() 80 | } 81 | } 82 | 83 | impl Drop for BackgroundRuntime { 84 | fn drop(&mut self) { 85 | self.is_open.store(false); 86 | self.shutdown_sink.send(()).ok(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/connection/transaction_stream.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::{fmt, iter}; 23 | 24 | use futures::{stream, Stream, StreamExt}; 25 | 26 | use super::network::transmitter::TransactionTransmitter; 27 | use crate::{ 28 | answer::{ConceptMap, Numeric}, 29 | common::Result, 30 | connection::message::{QueryRequest, QueryResponse, TransactionRequest, TransactionResponse}, 31 | error::InternalError, 32 | Options, TransactionType, 33 | }; 34 | 35 | pub(crate) struct TransactionStream { 36 | type_: TransactionType, 37 | options: Options, 38 | transaction_transmitter: TransactionTransmitter, 39 | } 40 | 41 | impl TransactionStream { 42 | pub(super) fn new( 43 | type_: TransactionType, 44 | options: Options, 45 | transaction_transmitter: TransactionTransmitter, 46 | ) -> Self { 47 | Self { type_, options, transaction_transmitter } 48 | } 49 | 50 | pub(crate) fn is_open(&self) -> bool { 51 | self.transaction_transmitter.is_open() 52 | } 53 | 54 | pub(crate) fn type_(&self) -> TransactionType { 55 | self.type_ 56 | } 57 | 58 | pub(crate) fn options(&self) -> &Options { 59 | &self.options 60 | } 61 | 62 | pub(crate) async fn commit(&self) -> Result { 63 | self.single(TransactionRequest::Commit).await?; 64 | Ok(()) 65 | } 66 | 67 | pub(crate) async fn rollback(&self) -> Result { 68 | self.single(TransactionRequest::Rollback).await?; 69 | Ok(()) 70 | } 71 | 72 | pub(crate) async fn define(&self, query: String, options: Options) -> Result { 73 | self.single(TransactionRequest::Query(QueryRequest::Define { query, options })).await?; 74 | Ok(()) 75 | } 76 | 77 | pub(crate) async fn undefine(&self, query: String, options: Options) -> Result { 78 | self.single(TransactionRequest::Query(QueryRequest::Undefine { query, options })).await?; 79 | Ok(()) 80 | } 81 | 82 | pub(crate) async fn delete(&self, query: String, options: Options) -> Result { 83 | self.single(TransactionRequest::Query(QueryRequest::Delete { query, options })).await?; 84 | Ok(()) 85 | } 86 | 87 | pub(crate) fn match_(&self, query: String, options: Options) -> Result>> { 88 | let stream = self.query_stream(QueryRequest::Match { query, options })?; 89 | Ok(stream.flat_map(|result| match result { 90 | Ok(QueryResponse::Match { answers }) => stream_iter(answers.into_iter().map(Ok)), 91 | Ok(other) => stream_once(Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into())), 92 | Err(err) => stream_once(Err(err)), 93 | })) 94 | } 95 | 96 | pub(crate) fn insert(&self, query: String, options: Options) -> Result>> { 97 | let stream = self.query_stream(QueryRequest::Insert { query, options })?; 98 | Ok(stream.flat_map(|result| match result { 99 | Ok(QueryResponse::Insert { answers }) => stream_iter(answers.into_iter().map(Ok)), 100 | Ok(other) => stream_once(Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into())), 101 | Err(err) => stream_once(Err(err)), 102 | })) 103 | } 104 | 105 | pub(crate) fn update(&self, query: String, options: Options) -> Result>> { 106 | let stream = self.query_stream(QueryRequest::Update { query, options })?; 107 | Ok(stream.flat_map(|result| match result { 108 | Ok(QueryResponse::Update { answers }) => stream_iter(answers.into_iter().map(Ok)), 109 | Ok(other) => stream_once(Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into())), 110 | Err(err) => stream_once(Err(err)), 111 | })) 112 | } 113 | 114 | pub(crate) async fn match_aggregate(&self, query: String, options: Options) -> Result { 115 | match self.query_single(QueryRequest::MatchAggregate { query, options }).await? { 116 | QueryResponse::MatchAggregate { answer } => Ok(answer), 117 | other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), 118 | } 119 | } 120 | 121 | async fn single(&self, req: TransactionRequest) -> Result { 122 | self.transaction_transmitter.single(req).await 123 | } 124 | 125 | async fn query_single(&self, req: QueryRequest) -> Result { 126 | match self.single(TransactionRequest::Query(req)).await? { 127 | TransactionResponse::Query(query) => Ok(query), 128 | other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), 129 | } 130 | } 131 | 132 | fn stream(&self, req: TransactionRequest) -> Result>> { 133 | self.transaction_transmitter.stream(req) 134 | } 135 | 136 | fn query_stream(&self, req: QueryRequest) -> Result>> { 137 | Ok(self.stream(TransactionRequest::Query(req))?.map(|response| match response { 138 | Ok(TransactionResponse::Query(query)) => Ok(query), 139 | Ok(other) => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), 140 | Err(err) => Err(err), 141 | })) 142 | } 143 | } 144 | 145 | impl fmt::Debug for TransactionStream { 146 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 147 | f.debug_struct("TransactionStream").field("type_", &self.type_).field("options", &self.options).finish() 148 | } 149 | } 150 | 151 | fn stream_once<'a, T: Send + 'a>(value: T) -> stream::BoxStream<'a, T> { 152 | stream_iter(iter::once(value)) 153 | } 154 | 155 | fn stream_iter<'a, T: Send + 'a>(iter: impl Iterator + Send + 'a) -> stream::BoxStream<'a, T> { 156 | Box::pin(stream::iter(iter)) 157 | } 158 | -------------------------------------------------------------------------------- /src/database/database.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::{fmt, future::Future, sync::RwLock, thread::sleep, time::Duration}; 23 | 24 | use itertools::Itertools; 25 | use log::{debug, error}; 26 | 27 | use crate::{ 28 | common::{ 29 | address::Address, 30 | error::ConnectionError, 31 | info::{DatabaseInfo, ReplicaInfo}, 32 | Error, Result, 33 | }, 34 | connection::ServerConnection, 35 | Connection, 36 | }; 37 | 38 | pub struct Database { 39 | name: String, 40 | replicas: RwLock>, 41 | connection: Connection, 42 | } 43 | 44 | impl Database { 45 | const PRIMARY_REPLICA_TASK_MAX_RETRIES: usize = 10; 46 | const FETCH_REPLICAS_MAX_RETRIES: usize = 10; 47 | const WAIT_FOR_PRIMARY_REPLICA_SELECTION: Duration = Duration::from_secs(2); 48 | 49 | pub(super) fn new(database_info: DatabaseInfo, connection: Connection) -> Result { 50 | let name = database_info.name.clone(); 51 | let replicas = RwLock::new(Replica::try_from_info(database_info, &connection)?); 52 | Ok(Self { name, replicas, connection }) 53 | } 54 | 55 | pub(super) async fn get(name: String, connection: Connection) -> Result { 56 | Ok(Self { 57 | name: name.to_string(), 58 | replicas: RwLock::new(Replica::fetch_all(name, connection.clone()).await?), 59 | connection, 60 | }) 61 | } 62 | 63 | pub fn name(&self) -> &str { 64 | self.name.as_str() 65 | } 66 | 67 | pub(super) fn connection(&self) -> &Connection { 68 | &self.connection 69 | } 70 | 71 | pub async fn delete(self) -> Result { 72 | self.run_on_primary_replica(|database, _, _| database.delete()).await 73 | } 74 | 75 | pub async fn schema(&self) -> Result { 76 | self.run_failsafe(|database, _, _| async move { database.schema().await }).await 77 | } 78 | 79 | pub async fn type_schema(&self) -> Result { 80 | self.run_failsafe(|database, _, _| async move { database.type_schema().await }).await 81 | } 82 | 83 | pub async fn rule_schema(&self) -> Result { 84 | self.run_failsafe(|database, _, _| async move { database.rule_schema().await }).await 85 | } 86 | 87 | pub(super) async fn run_failsafe(&self, task: F) -> Result 88 | where 89 | F: Fn(ServerDatabase, ServerConnection, bool) -> P, 90 | P: Future>, 91 | { 92 | match self.run_on_any_replica(&task).await { 93 | Err(Error::Connection(ConnectionError::ClusterReplicaNotPrimary())) => { 94 | debug!("Attempted to run on a non-primary replica, retrying on primary..."); 95 | self.run_on_primary_replica(&task).await 96 | } 97 | res => res, 98 | } 99 | } 100 | 101 | async fn run_on_any_replica(&self, task: F) -> Result 102 | where 103 | F: Fn(ServerDatabase, ServerConnection, bool) -> P, 104 | P: Future>, 105 | { 106 | let mut is_first_run = true; 107 | let replicas = self.replicas.read().unwrap().clone(); 108 | for replica in replicas { 109 | match task(replica.database.clone(), self.connection.connection(&replica.address)?.clone(), is_first_run) 110 | .await 111 | { 112 | Err(Error::Connection(ConnectionError::UnableToConnect())) => { 113 | debug!("Unable to connect to {}. Attempting next server.", replica.address); 114 | } 115 | res => return res, 116 | } 117 | is_first_run = false; 118 | } 119 | Err(self.connection.unable_to_connect_error()) 120 | } 121 | 122 | async fn run_on_primary_replica(&self, task: F) -> Result 123 | where 124 | F: Fn(ServerDatabase, ServerConnection, bool) -> P, 125 | P: Future>, 126 | { 127 | let mut primary_replica = 128 | if let Some(replica) = self.primary_replica() { replica } else { self.seek_primary_replica().await? }; 129 | 130 | for retry in 0..Self::PRIMARY_REPLICA_TASK_MAX_RETRIES { 131 | match task( 132 | primary_replica.database.clone(), 133 | self.connection.connection(&primary_replica.address)?.clone(), 134 | retry == 0, 135 | ) 136 | .await 137 | { 138 | Err(Error::Connection( 139 | ConnectionError::ClusterReplicaNotPrimary() | ConnectionError::UnableToConnect(), 140 | )) => { 141 | debug!("Primary replica error, waiting..."); 142 | Self::wait_for_primary_replica_selection().await; 143 | primary_replica = self.seek_primary_replica().await?; 144 | } 145 | res => return res, 146 | } 147 | } 148 | Err(self.connection.unable_to_connect_error()) 149 | } 150 | 151 | async fn seek_primary_replica(&self) -> Result { 152 | for _ in 0..Self::FETCH_REPLICAS_MAX_RETRIES { 153 | let replicas = Replica::fetch_all(self.name.clone(), self.connection.clone()).await?; 154 | *self.replicas.write().unwrap() = replicas; 155 | if let Some(replica) = self.primary_replica() { 156 | return Ok(replica); 157 | } 158 | Self::wait_for_primary_replica_selection().await; 159 | } 160 | Err(self.connection.unable_to_connect_error()) 161 | } 162 | 163 | fn primary_replica(&self) -> Option { 164 | self.replicas.read().unwrap().iter().filter(|r| r.is_primary).max_by_key(|r| r.term).cloned() 165 | } 166 | 167 | async fn wait_for_primary_replica_selection() { 168 | // FIXME: blocking sleep! Can't do agnostic async sleep. 169 | sleep(Self::WAIT_FOR_PRIMARY_REPLICA_SELECTION); 170 | } 171 | } 172 | 173 | impl fmt::Debug for Database { 174 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 175 | f.debug_struct("Database").field("name", &self.name).field("replicas", &self.replicas).finish() 176 | } 177 | } 178 | 179 | #[derive(Clone)] 180 | pub(super) struct Replica { 181 | address: Address, 182 | database_name: String, 183 | is_primary: bool, 184 | term: i64, 185 | is_preferred: bool, 186 | database: ServerDatabase, 187 | } 188 | 189 | impl Replica { 190 | fn new(name: String, metadata: ReplicaInfo, server_connection: ServerConnection) -> Self { 191 | Self { 192 | address: metadata.address, 193 | database_name: name.clone(), 194 | is_primary: metadata.is_primary, 195 | term: metadata.term, 196 | is_preferred: metadata.is_preferred, 197 | database: ServerDatabase::new(name, server_connection), 198 | } 199 | } 200 | 201 | fn try_from_info(database_info: DatabaseInfo, connection: &Connection) -> Result> { 202 | database_info 203 | .replicas 204 | .into_iter() 205 | .map(|replica| { 206 | let server_connection = connection.connection(&replica.address)?.clone(); 207 | Ok(Replica::new(database_info.name.clone(), replica, server_connection)) 208 | }) 209 | .try_collect() 210 | } 211 | 212 | async fn fetch_all(name: String, connection: Connection) -> Result> { 213 | for server_connection in connection.connections() { 214 | let res = server_connection.get_database_replicas(name.clone()).await; 215 | match res { 216 | Ok(res) => { 217 | return Replica::try_from_info(res, &connection); 218 | } 219 | Err(Error::Connection(ConnectionError::UnableToConnect())) => { 220 | error!( 221 | "Failed to fetch replica info for database '{}' from {}. Attempting next server.", 222 | name, 223 | server_connection.address() 224 | ); 225 | } 226 | Err(err) => return Err(err), 227 | } 228 | } 229 | Err(connection.unable_to_connect_error()) 230 | } 231 | } 232 | 233 | impl fmt::Debug for Replica { 234 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 235 | f.debug_struct("Replica") 236 | .field("address", &self.address) 237 | .field("database_name", &self.database_name) 238 | .field("is_primary", &self.is_primary) 239 | .field("term", &self.term) 240 | .field("is_preferred", &self.is_preferred) 241 | .finish() 242 | } 243 | } 244 | 245 | #[derive(Clone, Debug)] 246 | pub(super) struct ServerDatabase { 247 | name: String, 248 | connection: ServerConnection, 249 | } 250 | 251 | impl ServerDatabase { 252 | fn new(name: String, connection: ServerConnection) -> Self { 253 | ServerDatabase { name, connection } 254 | } 255 | 256 | pub(super) fn name(&self) -> &str { 257 | self.name.as_str() 258 | } 259 | 260 | pub(super) fn connection(&self) -> &ServerConnection { 261 | &self.connection 262 | } 263 | 264 | async fn delete(self) -> Result { 265 | self.connection.delete_database(self.name).await 266 | } 267 | 268 | async fn schema(&self) -> Result { 269 | self.connection.database_schema(self.name.clone()).await 270 | } 271 | 272 | async fn type_schema(&self) -> Result { 273 | self.connection.database_type_schema(self.name.clone()).await 274 | } 275 | 276 | async fn rule_schema(&self) -> Result { 277 | self.connection.database_rule_schema(self.name.clone()).await 278 | } 279 | } 280 | 281 | impl fmt::Display for ServerDatabase { 282 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 283 | write!(f, "{}", self.name) 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /src/database/database_manager.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::future::Future; 23 | 24 | use super::{database::ServerDatabase, Database}; 25 | use crate::{ 26 | common::{error::ConnectionError, Result}, 27 | connection::ServerConnection, 28 | Connection, 29 | }; 30 | 31 | #[derive(Clone, Debug)] 32 | pub struct DatabaseManager { 33 | connection: Connection, 34 | } 35 | 36 | impl DatabaseManager { 37 | pub fn new(connection: Connection) -> Self { 38 | Self { connection } 39 | } 40 | 41 | pub async fn get(&self, name: impl Into) -> Result { 42 | Database::get(name.into(), self.connection.clone()).await 43 | } 44 | 45 | pub async fn contains(&self, name: impl Into) -> Result { 46 | self.run_failsafe(name.into(), move |database, server_connection, _| async move { 47 | server_connection.database_exists(database.name().to_owned()).await 48 | }) 49 | .await 50 | } 51 | 52 | pub async fn create(&self, name: impl Into) -> Result { 53 | self.run_failsafe(name.into(), |database, server_connection, _| async move { 54 | server_connection.create_database(database.name().to_owned()).await 55 | }) 56 | .await 57 | } 58 | 59 | pub async fn all(&self) -> Result> { 60 | let mut error_buffer = Vec::with_capacity(self.connection.server_count()); 61 | for server_connection in self.connection.connections() { 62 | match server_connection.all_databases().await { 63 | Ok(list) => { 64 | return list.into_iter().map(|db_info| Database::new(db_info, self.connection.clone())).collect() 65 | } 66 | Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), 67 | } 68 | } 69 | Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? 70 | } 71 | 72 | async fn run_failsafe(&self, name: String, task: F) -> Result 73 | where 74 | F: Fn(ServerDatabase, ServerConnection, bool) -> P, 75 | P: Future>, 76 | { 77 | Database::get(name, self.connection.clone()).await?.run_failsafe(&task).await 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/database/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | mod database; 23 | mod database_manager; 24 | mod query; 25 | mod session; 26 | mod transaction; 27 | 28 | pub use self::{database::Database, database_manager::DatabaseManager, session::Session, transaction::Transaction}; 29 | -------------------------------------------------------------------------------- /src/database/query.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::sync::Arc; 23 | 24 | use futures::Stream; 25 | 26 | use crate::{ 27 | answer::{ConceptMap, Numeric}, 28 | common::Result, 29 | connection::TransactionStream, 30 | Options, 31 | }; 32 | 33 | #[derive(Debug)] 34 | pub struct QueryManager { 35 | transaction_stream: Arc, 36 | } 37 | 38 | impl QueryManager { 39 | pub(super) fn new(transaction_stream: Arc) -> QueryManager { 40 | QueryManager { transaction_stream } 41 | } 42 | 43 | pub async fn define(&self, query: &str) -> Result { 44 | self.define_with_options(query, Options::new()).await 45 | } 46 | 47 | pub async fn define_with_options(&self, query: &str, options: Options) -> Result { 48 | self.transaction_stream.define(query.to_string(), options).await 49 | } 50 | 51 | pub async fn undefine(&self, query: &str) -> Result { 52 | self.undefine_with_options(query, Options::new()).await 53 | } 54 | 55 | pub async fn undefine_with_options(&self, query: &str, options: Options) -> Result { 56 | self.transaction_stream.undefine(query.to_string(), options).await 57 | } 58 | 59 | pub async fn delete(&self, query: &str) -> Result { 60 | self.delete_with_options(query, Options::new()).await 61 | } 62 | 63 | pub async fn delete_with_options(&self, query: &str, options: Options) -> Result { 64 | self.transaction_stream.delete(query.to_string(), options).await 65 | } 66 | 67 | pub fn match_(&self, query: &str) -> Result>> { 68 | self.match_with_options(query, Options::new()) 69 | } 70 | 71 | pub fn match_with_options(&self, query: &str, options: Options) -> Result>> { 72 | self.transaction_stream.match_(query.to_string(), options) 73 | } 74 | 75 | pub fn insert(&self, query: &str) -> Result>> { 76 | self.insert_with_options(query, Options::new()) 77 | } 78 | 79 | pub fn insert_with_options(&self, query: &str, options: Options) -> Result>> { 80 | self.transaction_stream.insert(query.to_string(), options) 81 | } 82 | 83 | pub fn update(&self, query: &str) -> Result>> { 84 | self.update_with_options(query, Options::new()) 85 | } 86 | 87 | pub fn update_with_options(&self, query: &str, options: Options) -> Result>> { 88 | self.transaction_stream.update(query.to_string(), options) 89 | } 90 | 91 | pub async fn match_aggregate(&self, query: &str) -> Result { 92 | self.match_aggregate_with_options(query, Options::new()).await 93 | } 94 | 95 | pub async fn match_aggregate_with_options(&self, query: &str, options: Options) -> Result { 96 | self.transaction_stream.match_aggregate(query.to_string(), options).await 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/database/session.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::sync::RwLock; 23 | 24 | use crossbeam::atomic::AtomicCell; 25 | use log::warn; 26 | 27 | use crate::{ 28 | common::{error::ConnectionError, info::SessionInfo, Result, SessionType, TransactionType}, 29 | Database, Options, Transaction, 30 | }; 31 | 32 | #[derive(Debug)] 33 | pub struct Session { 34 | database: Database, 35 | server_session_info: RwLock, 36 | session_type: SessionType, 37 | is_open: AtomicCell, 38 | } 39 | 40 | impl Drop for Session { 41 | fn drop(&mut self) { 42 | if let Err(err) = self.force_close() { 43 | warn!("Error encountered while closing session: {}", err); 44 | } 45 | } 46 | } 47 | 48 | impl Session { 49 | pub async fn new(database: Database, session_type: SessionType) -> Result { 50 | let server_session_info = RwLock::new( 51 | database 52 | .run_failsafe(|database, _, _| async move { 53 | database 54 | .connection() 55 | .open_session(database.name().to_owned(), session_type, Options::default()) 56 | .await 57 | }) 58 | .await?, 59 | ); 60 | 61 | Ok(Self { database, session_type, server_session_info, is_open: AtomicCell::new(true) }) 62 | } 63 | 64 | pub fn database_name(&self) -> &str { 65 | self.database.name() 66 | } 67 | 68 | pub fn type_(&self) -> SessionType { 69 | self.session_type 70 | } 71 | 72 | pub fn is_open(&self) -> bool { 73 | self.is_open.load() 74 | } 75 | 76 | pub fn force_close(&self) -> Result { 77 | if self.is_open.compare_exchange(true, false).is_ok() { 78 | let session_info = self.server_session_info.write().unwrap(); 79 | let connection = self.database.connection().connection(&session_info.address).unwrap(); 80 | connection.close_session(session_info.session_id.clone())?; 81 | } 82 | Ok(()) 83 | } 84 | 85 | pub async fn transaction(&self, transaction_type: TransactionType) -> Result { 86 | self.transaction_with_options(transaction_type, Options::new()).await 87 | } 88 | 89 | pub async fn transaction_with_options( 90 | &self, 91 | transaction_type: TransactionType, 92 | options: Options, 93 | ) -> Result { 94 | if !self.is_open() { 95 | return Err(ConnectionError::SessionIsClosed().into()); 96 | } 97 | 98 | let (session_info, transaction_stream) = self 99 | .database 100 | .run_failsafe(|database, _, is_first_run| { 101 | let session_info = self.server_session_info.read().unwrap().clone(); 102 | let session_type = self.session_type; 103 | let options = options.clone(); 104 | async move { 105 | let connection = database.connection(); 106 | let session_info = if is_first_run { 107 | session_info 108 | } else { 109 | connection.open_session(database.name().to_owned(), session_type, options.clone()).await? 110 | }; 111 | Ok(( 112 | session_info.clone(), 113 | connection 114 | .open_transaction( 115 | session_info.session_id, 116 | transaction_type, 117 | options, 118 | session_info.network_latency, 119 | ) 120 | .await?, 121 | )) 122 | } 123 | }) 124 | .await?; 125 | 126 | *self.server_session_info.write().unwrap() = session_info; 127 | Ok(Transaction::new(transaction_stream)) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/database/transaction.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::{fmt, marker::PhantomData, sync::Arc}; 23 | 24 | use super::query::QueryManager; 25 | use crate::{ 26 | common::{Result, TransactionType}, 27 | connection::TransactionStream, 28 | Options, 29 | }; 30 | 31 | pub struct Transaction<'a> { 32 | type_: TransactionType, 33 | options: Options, 34 | 35 | query: QueryManager, 36 | transaction_stream: Arc, 37 | 38 | _lifetime_guard: PhantomData<&'a ()>, 39 | } 40 | 41 | impl Transaction<'_> { 42 | pub(super) fn new(transaction_stream: TransactionStream) -> Self { 43 | let transaction_stream = Arc::new(transaction_stream); 44 | Transaction { 45 | type_: transaction_stream.type_(), 46 | options: transaction_stream.options().clone(), 47 | query: QueryManager::new(transaction_stream.clone()), 48 | transaction_stream, 49 | _lifetime_guard: PhantomData::default(), 50 | } 51 | } 52 | 53 | pub fn is_open(&self) -> bool { 54 | self.transaction_stream.is_open() 55 | } 56 | 57 | pub fn type_(&self) -> TransactionType { 58 | self.type_ 59 | } 60 | 61 | pub fn query(&self) -> &QueryManager { 62 | &self.query 63 | } 64 | 65 | pub async fn commit(self) -> Result { 66 | self.transaction_stream.commit().await 67 | } 68 | 69 | pub async fn rollback(&self) -> Result { 70 | self.transaction_stream.rollback().await 71 | } 72 | } 73 | 74 | impl fmt::Debug for Transaction<'_> { 75 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 76 | f.debug_struct("Transaction").field("type_", &self.type_).field("options", &self.options).finish() 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | mod answer; 23 | mod common; 24 | pub mod concept; 25 | mod connection; 26 | mod database; 27 | 28 | pub use self::{ 29 | common::{error, Credential, Error, Options, Result, SessionType, TransactionType}, 30 | connection::Connection, 31 | database::{Database, DatabaseManager, Session, Transaction}, 32 | }; 33 | -------------------------------------------------------------------------------- /tests/BUILD: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2022 Vaticle 3 | # 4 | # Licensed to the Apache Software Foundation (ASF) under one 5 | # or more contributor license agreements. See the NOTICE file 6 | # distributed with this work for additional information 7 | # regarding copyright ownership. The ASF licenses this file 8 | # to you under the Apache License, Version 2.0 (the 9 | # "License"); you may not use this file except in compliance 10 | # with the License. You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, 15 | # software distributed under the License is distributed on an 16 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | # KIND, either express or implied. See the License for the 18 | # specific language governing permissions and limitations 19 | # under the License. 20 | # 21 | 22 | package(default_visibility = ["//visibility:public"]) 23 | 24 | load("@rules_rust//rust:defs.bzl", "rust_test", "rustfmt_test") 25 | load("@vaticle_bazel_distribution//artifact:rules.bzl", "artifact_extractor") 26 | load("@vaticle_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") 27 | load("@vaticle_typedb_common//runner:rules.bzl", "native_typedb_artifact") 28 | 29 | rust_test( 30 | name = "tests", 31 | srcs = glob(["**/*.rs"]), 32 | deps = [ 33 | "//:typedb_client", 34 | "@vaticle_typeql//rust:typeql_lang", 35 | "@crates//:async-std", 36 | "@crates//:chrono", 37 | "@crates//:cucumber", 38 | "@crates//:futures", 39 | "@crates//:serial_test", 40 | "@crates//:smol", 41 | "@crates//:tokio", 42 | ], 43 | data = [ 44 | "@vaticle_typedb_behaviour//connection:database.feature", 45 | "@vaticle_typedb_behaviour//connection:session.feature", 46 | "@vaticle_typedb_behaviour//connection:transaction.feature", 47 | ], 48 | ) 49 | 50 | native_typedb_artifact( 51 | name = "native-typedb-artifact", 52 | mac_artifact = "@vaticle_typedb_artifact_mac//file", 53 | linux_artifact = "@vaticle_typedb_artifact_linux//file", 54 | windows_artifact = "@vaticle_typedb_artifact_windows//file", 55 | output = "typedb-artifact.tar.gz" 56 | ) 57 | 58 | native_typedb_artifact( 59 | name = "native-typedb-cluster-artifact", 60 | mac_artifact = "@vaticle_typedb_cluster_artifact_mac//file", 61 | linux_artifact = "@vaticle_typedb_cluster_artifact_linux//file", 62 | windows_artifact = "@vaticle_typedb_cluster_artifact_windows//file", 63 | output = "typedb-cluster-artifact.tar.gz" 64 | ) 65 | 66 | artifact_extractor( 67 | name = "typedb-extractor", 68 | artifact = ":native-typedb-artifact", 69 | ) 70 | 71 | artifact_extractor( 72 | name = "typedb-cluster-extractor", 73 | artifact = ":native-typedb-cluster-artifact", 74 | ) 75 | 76 | rustfmt_test( 77 | name = "rustfmt_test", 78 | targets = ["tests"] 79 | ) 80 | 81 | checkstyle_test( 82 | name = "checkstyle", 83 | include = glob(["*", "**/*"]), 84 | license_type = "apache-header", 85 | size = "small", 86 | ) 87 | -------------------------------------------------------------------------------- /tests/behaviour/connection/database/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | mod steps; 23 | 24 | use serial_test::serial; 25 | 26 | use crate::behaviour::Context; 27 | 28 | #[tokio::test] 29 | #[serial] 30 | async fn test() { 31 | // Bazel specific path: when running the test in bazel, the external data from 32 | // @vaticle_typedb_behaviour is stored in a directory that is a sibling to 33 | // the working directory. 34 | assert!(Context::test("../vaticle_typedb_behaviour/connection/database.feature").await); 35 | } 36 | -------------------------------------------------------------------------------- /tests/behaviour/connection/database/steps.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::collections::HashSet; 23 | 24 | use cucumber::{gherkin::Step, given, then, when}; 25 | use futures::{future::try_join_all, TryFutureExt}; 26 | use typedb_client::Database; 27 | 28 | use crate::{ 29 | behaviour::{util, Context}, 30 | generic_step_impl, 31 | }; 32 | 33 | generic_step_impl! { 34 | #[step(expr = "connection create database: {word}")] 35 | async fn connection_create_database(context: &mut Context, name: String) { 36 | context.databases.create(name).await.unwrap(); 37 | } 38 | 39 | #[step(expr = "connection create database(s):")] 40 | async fn connection_create_databases(context: &mut Context, step: &Step) { 41 | for name in util::iter_table(step) { 42 | context.databases.create(name).await.unwrap(); 43 | } 44 | } 45 | 46 | #[step("connection create databases in parallel:")] 47 | async fn connection_create_databases_in_parallel(context: &mut Context, step: &Step) { 48 | try_join_all(util::iter_table(step).map(|name| context.databases.create(name))).await.unwrap(); 49 | } 50 | 51 | #[step(expr = "connection delete database: {word}")] 52 | async fn connection_delete_database(context: &mut Context, name: String) { 53 | context.databases.get(name).and_then(Database::delete).await.unwrap(); 54 | } 55 | 56 | #[step(expr = "connection delete database(s):")] 57 | async fn connection_delete_databases(context: &mut Context, step: &Step) { 58 | for name in util::iter_table(step) { 59 | context.databases.get(name).and_then(Database::delete).await.unwrap(); 60 | } 61 | } 62 | 63 | #[step(expr = "connection delete databases in parallel:")] 64 | async fn connection_delete_databases_in_parallel(context: &mut Context, step: &Step) { 65 | try_join_all(util::iter_table(step).map(|name| context.databases.get(name).and_then(Database::delete))) 66 | .await 67 | .unwrap(); 68 | } 69 | 70 | #[step(expr = "connection delete database; throws exception: {word}")] 71 | async fn connection_delete_database_throws_exception(context: &mut Context, name: String) { 72 | assert!(context.databases.get(name).and_then(Database::delete).await.is_err()); 73 | } 74 | 75 | #[step(expr = "connection delete database(s); throws exception")] 76 | async fn connection_delete_databases_throws_exception(context: &mut Context, step: &Step) { 77 | for name in util::iter_table(step) { 78 | assert!(context.databases.get(name).and_then(Database::delete).await.is_err()); 79 | } 80 | } 81 | 82 | #[step(expr = "connection has database: {word}")] 83 | async fn connection_has_database(context: &mut Context, name: String) { 84 | assert!(context.databases.contains(name).await.unwrap()); 85 | } 86 | 87 | #[step(expr = "connection has database(s):")] 88 | async fn connection_has_databases(context: &mut Context, step: &Step) { 89 | let names: HashSet = util::iter_table(step).map(|name| name.to_owned()).collect(); 90 | let all_databases = context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect(); 91 | assert_eq!(names, all_databases); 92 | } 93 | 94 | #[step(expr = "connection does not have database: {word}")] 95 | async fn connection_does_not_have_database(context: &mut Context, name: String) { 96 | assert!(!context.databases.contains(name).await.unwrap()); 97 | } 98 | 99 | #[step(expr = "connection does not have database(s):")] 100 | async fn connection_does_not_have_databases(context: &mut Context, step: &Step) { 101 | let all_databases: HashSet = 102 | context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect(); 103 | for name in util::iter_table(step) { 104 | assert!(!all_databases.contains(name)); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /tests/behaviour/connection/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | mod database; 23 | mod session; 24 | mod steps; 25 | mod transaction; 26 | -------------------------------------------------------------------------------- /tests/behaviour/connection/session/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | mod steps; 23 | 24 | use serial_test::serial; 25 | 26 | use crate::behaviour::Context; 27 | 28 | #[tokio::test] 29 | #[serial] 30 | async fn test() { 31 | // Bazel specific path: when running the test in bazel, the external data from 32 | // @vaticle_typedb_behaviour is stored in a directory that is a sibling to 33 | // the working directory. 34 | assert!(Context::test("../vaticle_typedb_behaviour/connection/session.feature").await); 35 | } 36 | -------------------------------------------------------------------------------- /tests/behaviour/connection/session/steps.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use cucumber::{gherkin::Step, given, then, when}; 23 | use futures::{future::try_join_all, TryFutureExt}; 24 | use typedb_client::{Session, SessionType}; 25 | 26 | use crate::{ 27 | behaviour::{util, Context}, 28 | generic_step_impl, 29 | }; 30 | 31 | generic_step_impl! { 32 | #[step(expr = "connection open schema session for database: {word}")] 33 | async fn connection_open_schema_session_for_database(context: &mut Context, name: String) { 34 | context 35 | .session_trackers 36 | .push(Session::new(context.databases.get(name).await.unwrap(), SessionType::Schema).await.unwrap().into()); 37 | } 38 | 39 | #[step(expr = "connection open (data )session for database: {word}")] 40 | async fn connection_open_data_session_for_database(context: &mut Context, name: String) { 41 | context 42 | .session_trackers 43 | .push(Session::new(context.databases.get(name).await.unwrap(), SessionType::Data).await.unwrap().into()); 44 | } 45 | 46 | #[step(expr = "connection open schema session(s) for database(s):")] 47 | async fn connection_open_schema_sessions_for_databases(context: &mut Context, step: &Step) { 48 | for name in util::iter_table(step) { 49 | context.session_trackers.push( 50 | Session::new(context.databases.get(name).await.unwrap(), SessionType::Schema).await.unwrap().into(), 51 | ); 52 | } 53 | } 54 | 55 | #[step(expr = "connection open (data )session(s) for database(s):")] 56 | async fn connection_open_data_sessions_for_databases(context: &mut Context, step: &Step) { 57 | for name in util::iter_table(step) { 58 | context 59 | .session_trackers 60 | .push(Session::new(context.databases.get(name).await.unwrap(), SessionType::Data).await.unwrap().into()); 61 | } 62 | } 63 | 64 | #[step(expr = "connection open (data )sessions in parallel for databases:")] 65 | async fn connection_open_data_sessions_in_parallel_for_databases(context: &mut Context, step: &Step) { 66 | let new_sessions = try_join_all( 67 | util::iter_table(step) 68 | .map(|name| context.databases.get(name).and_then(|db| Session::new(db, SessionType::Data))), 69 | ) 70 | .await 71 | .unwrap(); 72 | context.session_trackers.extend(new_sessions.into_iter().map(|session| session.into())); 73 | } 74 | 75 | #[step(expr = "session(s) is/are null: {word}")] 76 | #[step(expr = "sessions in parallel are null: {word}")] 77 | async fn sessions_are_null(_context: &mut Context, is_null: bool) { 78 | assert!(!is_null); // Rust sessions are not nullable 79 | } 80 | 81 | #[step(expr = "session(s) is/are open: {word}")] 82 | #[step(expr = "sessions in parallel are open: {word}")] 83 | async fn sessions_are_open(context: &mut Context, is_open: bool) { 84 | for session_tracker in &context.session_trackers { 85 | assert_eq!(session_tracker.session().is_open(), is_open); 86 | } 87 | } 88 | 89 | #[step(expr = "session(s) has/have database: {word}")] 90 | async fn sessions_have_database(context: &mut Context, name: String) { 91 | assert_eq!(context.session_trackers.get(0).unwrap().session().database_name(), name) 92 | } 93 | 94 | #[step(expr = "session(s) has/have database(s):")] 95 | #[step(expr = "sessions in parallel have databases:")] 96 | async fn sessions_have_databases(context: &mut Context, step: &Step) { 97 | assert_eq!(step.table.as_ref().unwrap().rows.len(), context.session_trackers.len()); 98 | for (name, session_tracker) in util::iter_table(step).zip(&context.session_trackers) { 99 | assert_eq!(name, session_tracker.session().database_name()); 100 | } 101 | } 102 | 103 | #[step(expr = "set session option {word} to: {word}")] 104 | async fn set_session_option_to(_context: &mut Context, _option: String, _value: String) { 105 | todo!() 106 | // if (!optionSetters.containsKey(option)) { 107 | // throw new RuntimeException("Unrecognised option: " + option); 108 | // } 109 | // optionSetters.get(option).accept(sessionOptions, value); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /tests/behaviour/connection/steps.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use cucumber::{given, then, when}; 23 | 24 | use crate::{behaviour::Context, generic_step_impl}; 25 | 26 | generic_step_impl! { 27 | #[step("connection has been opened")] 28 | async fn connection_has_been_opened(_: &mut Context) {} 29 | 30 | #[step("connection does not have any database")] 31 | async fn connection_does_not_have_any_database(context: &mut Context) { 32 | assert!(context.databases.all().await.unwrap().is_empty()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/behaviour/connection/transaction/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | mod steps; 23 | 24 | use serial_test::serial; 25 | 26 | use crate::behaviour::Context; 27 | 28 | #[tokio::test] 29 | #[serial] 30 | async fn test() { 31 | // Bazel specific path: when running the test in bazel, the external data from 32 | // @vaticle_typedb_behaviour is stored in a directory that is a sibling to 33 | // the working directory. 34 | assert!(Context::test("../vaticle_typedb_behaviour/connection/transaction.feature").await); 35 | } 36 | -------------------------------------------------------------------------------- /tests/behaviour/connection/transaction/steps.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use cucumber::{gherkin::Step, given, then, when}; 23 | use typedb_client::TransactionType; 24 | 25 | use crate::{ 26 | behaviour::{util, Context}, 27 | generic_step_impl, 28 | }; 29 | 30 | fn parse_transaction_type(type_: &str) -> TransactionType { 31 | match type_ { 32 | "write" => TransactionType::Write, 33 | "read" => TransactionType::Read, 34 | _ => unreachable!("`{type_}` is not a valid transaction type"), 35 | } 36 | } 37 | 38 | generic_step_impl! { 39 | // =============================================// 40 | // sequential sessions, sequential transactions // 41 | // =============================================// 42 | 43 | #[step(expr = "(for each )session(,) open(s) transaction(s) of type: {word}")] 44 | async fn session_opens_transaction_of_type(context: &mut Context, type_: String) { 45 | for session_tracker in &mut context.session_trackers { 46 | session_tracker.open_transaction(parse_transaction_type(&type_)).await.unwrap(); 47 | } 48 | } 49 | 50 | #[step(expr = "(for each )session(,) open transaction(s) of type:")] 51 | async fn for_each_session_open_transactions_of_type(context: &mut Context, step: &Step) { 52 | for type_ in util::iter_table(step) { 53 | let transaction_type = parse_transaction_type(&type_); 54 | for session_tracker in &mut context.session_trackers { 55 | session_tracker.open_transaction(transaction_type).await.unwrap(); 56 | } 57 | } 58 | } 59 | 60 | #[step(expr = "(for each )session(,) open transaction(s) of type; throws exception: {word}")] 61 | async fn for_each_session_open_transactions_of_type_throws_exception(context: &mut Context, type_: String) { 62 | let transaction_type = parse_transaction_type(&type_); 63 | for session_tracker in &mut context.session_trackers { 64 | assert!(session_tracker.open_transaction(transaction_type).await.is_err()); 65 | } 66 | } 67 | 68 | #[step(expr = "(for each )session(,) open transaction(s) of type; throws exception")] 69 | async fn for_each_session_open_transactions_of_type_throws_exception_table(context: &mut Context, step: &Step) { 70 | for type_ in util::iter_table(step) { 71 | let transaction_type = parse_transaction_type(&type_); 72 | for session_tracker in &mut context.session_trackers { 73 | assert!(session_tracker.open_transaction(transaction_type).await.is_err()); 74 | } 75 | } 76 | } 77 | 78 | #[step(expr = "(for each )session(,) transaction(s) is/are null: {word}")] 79 | #[step(expr = "for each session, transactions in parallel are null: {word}")] 80 | #[step(expr = "for each session in parallel, transactions in parallel are null: {word}")] 81 | async fn for_each_session_transactions_are_null(_context: &mut Context, is_null: bool) { 82 | assert!(!is_null); // Rust transactions are not nullable 83 | } 84 | 85 | #[step(expr = "(for each )session(,) transaction(s) is/are open: {word}")] 86 | #[step(expr = "for each session, transactions in parallel are open: {word}")] 87 | #[step(expr = "for each session in parallel, transactions in parallel are open: {word}")] 88 | async fn for_each_session_transactions_are_open(context: &mut Context, is_open: bool) { 89 | for session_tracker in &context.session_trackers { 90 | for transaction in session_tracker.transactions() { 91 | assert_eq!(transaction.is_open(), is_open); 92 | } 93 | } 94 | } 95 | 96 | #[step(expr = "transaction commits")] 97 | async fn transaction_commits(context: &mut Context) { 98 | context.take_transaction().commit().await.unwrap(); 99 | } 100 | 101 | #[step(expr = "transaction commits; throws exception")] 102 | async fn transaction_commits_throws(context: &mut Context) { 103 | assert!(context.take_transaction().commit().await.is_err()); 104 | } 105 | 106 | #[step(expr = "transaction commits; throws exception containing {string}")] 107 | async fn transaction_commits_throws_exception(context: &mut Context, exception: String) { 108 | let res = context.take_transaction().commit().await; 109 | assert!(res.is_err()); 110 | assert!(res.unwrap_err().to_string().contains(&exception)); 111 | } 112 | 113 | #[step(expr = "(for each )session(,) transaction(s) commit(s)")] 114 | async fn for_each_session_transactions_commit(context: &mut Context) { 115 | for session_tracker in &mut context.session_trackers { 116 | for transaction in session_tracker.transactions_mut().drain(..) { 117 | transaction.commit().await.unwrap(); 118 | } 119 | } 120 | } 121 | 122 | #[step(expr = "(for each )session(,) transaction(s) commit(s); throws exception")] 123 | async fn for_each_session_transactions_commits_throws_exception(context: &mut Context) { 124 | for session_tracker in &mut context.session_trackers { 125 | for transaction in session_tracker.transactions_mut().drain(..) { 126 | assert!(transaction.commit().await.is_err()); 127 | } 128 | } 129 | } 130 | 131 | #[step(expr = "(for each )session(,) transaction close(s)")] 132 | async fn for_each_session_transaction_closes(context: &mut Context) { 133 | for session_tracker in &mut context.session_trackers { 134 | session_tracker.transactions_mut().clear(); 135 | } 136 | } 137 | 138 | #[step(expr = "(for each )session(,) transaction(s) has/have type: {word}")] 139 | async fn for_each_session_transactions_have_type(context: &mut Context, type_: String) { 140 | let transaction_type = parse_transaction_type(&type_); 141 | for session_tracker in &context.session_trackers { 142 | assert_eq!(session_tracker.transactions().len(), 1); 143 | assert_eq!(transaction_type, session_tracker.transaction().type_()); 144 | } 145 | } 146 | 147 | #[step(expr = "(for each )session(,) transaction(s) has/have type:")] 148 | #[step(expr = "for each session, transactions in parallel have type:")] 149 | async fn for_each_session_transactions_have_types(context: &mut Context, step: &Step) { 150 | let types: Vec = util::iter_table(step).map(parse_transaction_type).collect(); 151 | for session_tracker in &context.session_trackers { 152 | assert_eq!(types.len(), session_tracker.transactions().len()); 153 | for (type_, transaction) in types.iter().zip(session_tracker.transactions()) { 154 | assert_eq!(*type_, transaction.type_()); 155 | } 156 | } 157 | } 158 | 159 | // ===========================================// 160 | // sequential sessions, parallel transactions // 161 | // ===========================================// 162 | 163 | #[step(expr = "for each session, open transaction(s) in parallel of type:")] 164 | async fn for_each_session_open_transactions_in_parallel_of_type(context: &mut Context, step: &Step) { 165 | for type_ in util::iter_table(step) { 166 | let transaction_type = parse_transaction_type(&type_); 167 | for session_tracker in &mut context.session_trackers { 168 | // FIXME parallel 169 | session_tracker.open_transaction(transaction_type).await.unwrap(); 170 | } 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /tests/behaviour/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | mod connection; 23 | pub mod session_tracker; 24 | mod typeql; 25 | mod util; 26 | 27 | use cucumber::{StatsWriter, World}; 28 | use futures::future::try_join_all; 29 | use typedb_client::{Connection, Database, DatabaseManager, Result as TypeDBResult, Transaction}; 30 | 31 | use self::session_tracker::SessionTracker; 32 | 33 | #[derive(Debug, World)] 34 | pub struct Context { 35 | pub connection: Connection, 36 | pub databases: DatabaseManager, 37 | pub session_trackers: Vec, 38 | } 39 | 40 | impl Context { 41 | async fn test(glob: &'static str) -> bool { 42 | !Self::cucumber() 43 | .repeat_failed() 44 | .fail_on_skipped() 45 | .max_concurrent_scenarios(Some(1)) 46 | .with_default_cli() 47 | .after(|_, _, _, _, context| { 48 | Box::pin(async { 49 | context.unwrap().after_scenario().await.unwrap(); 50 | }) 51 | }) 52 | .filter_run(glob, |_, _, sc| !sc.tags.iter().any(Self::is_ignore_tag)) 53 | .await 54 | .execution_has_failed() 55 | } 56 | 57 | fn is_ignore_tag(t: &String) -> bool { 58 | t == "ignore" || t == "ignore-typedb" || t == "ignore-client-rust" || t == "ignore-typedb-client-rust" 59 | } 60 | 61 | async fn after_scenario(&self) -> TypeDBResult { 62 | try_join_all(self.databases.all().await.unwrap().into_iter().map(Database::delete)).await?; 63 | Ok(()) 64 | } 65 | 66 | fn transaction(&self) -> &Transaction { 67 | self.session_trackers.get(0).unwrap().transaction() 68 | } 69 | 70 | pub fn take_transaction(&mut self) -> Transaction { 71 | self.session_trackers.get_mut(0).unwrap().take_transaction() 72 | } 73 | } 74 | 75 | impl Default for Context { 76 | fn default() -> Self { 77 | let connection = Connection::new_plaintext("0.0.0.0:1729").unwrap(); 78 | let databases = DatabaseManager::new(connection.clone()); 79 | Self { connection, databases, session_trackers: Vec::new() } 80 | } 81 | } 82 | 83 | #[macro_export] 84 | macro_rules! generic_step_impl { 85 | {$($(#[step($pattern:expr)])+ $async:ident fn $fn_name:ident $args:tt $body:tt)+} => { 86 | $($( 87 | #[given($pattern)] 88 | #[when($pattern)] 89 | #[then($pattern)] 90 | )* 91 | $async fn $fn_name $args $body 92 | )* 93 | }; 94 | } 95 | -------------------------------------------------------------------------------- /tests/behaviour/session_tracker.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::fmt; 23 | 24 | use futures::future::try_join_all; 25 | use typedb_client::{Session, Transaction, TransactionType}; 26 | 27 | pub struct SessionTracker { 28 | session: Session, 29 | transactions: Vec>, 30 | } 31 | 32 | impl fmt::Debug for SessionTracker { 33 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 34 | f.debug_struct("SessionTracker").field("session", &self.session).finish() 35 | } 36 | } 37 | 38 | impl From for SessionTracker { 39 | fn from(session: Session) -> Self { 40 | Self { session, transactions: Vec::new() } 41 | } 42 | } 43 | 44 | impl Drop for SessionTracker { 45 | fn drop(&mut self) { 46 | self.transactions.clear(); // ensure transactions are dropped before the sessions 47 | } 48 | } 49 | 50 | impl SessionTracker { 51 | pub fn session(&self) -> &Session { 52 | &self.session 53 | } 54 | 55 | pub async fn open_transaction(&mut self, transaction_type: TransactionType) -> typedb_client::Result { 56 | unsafe { 57 | // SAFETY: the transactions tracked by the SessionTracker instance borrow SessionTracker::session. 58 | // As long as SessionTracker is alive, the transactions are valid. 59 | let transaction = self.session.transaction(transaction_type).await?; 60 | self.transactions.push(std::mem::transmute(transaction)); 61 | } 62 | Ok(()) 63 | } 64 | 65 | pub fn has_transactions(&self) -> bool { 66 | !self.transactions.is_empty() 67 | } 68 | 69 | pub fn transaction(&self) -> &Transaction { 70 | // SAFETY: the returned transaction borrows SessionTracker, which from the POV of lifetimes is equivalent to 71 | // borrowing SessionTracker::session. 72 | unsafe { std::mem::transmute(self.transactions.get(0).unwrap()) } 73 | } 74 | 75 | pub fn take_transaction(&mut self) -> Transaction { 76 | // SAFETY: the returned transaction borrows SessionTracker, which from the POV of lifetimes is equivalent to 77 | // borrowing SessionTracker::session. 78 | unsafe { std::mem::transmute(self.transactions.remove(0)) } 79 | } 80 | 81 | pub fn transactions(&self) -> &Vec { 82 | // SAFETY: the returned transactions borrow SessionTracker, which from the POV of lifetimes is equivalent to 83 | // borrowing SessionTracker::session. 84 | unsafe { std::mem::transmute(&self.transactions) } 85 | } 86 | 87 | pub fn transactions_mut(&mut self) -> &mut Vec { 88 | // SAFETY: the returned transactions borrow SessionTracker, which from the POV of lifetimes is equivalent to 89 | // borrowing SessionTracker::session. 90 | unsafe { std::mem::transmute(&mut self.transactions) } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /tests/behaviour/typeql/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | mod steps; 23 | -------------------------------------------------------------------------------- /tests/behaviour/typeql/steps.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use cucumber::{gherkin::Step, given, then, when}; 23 | use futures::{TryFutureExt, TryStreamExt}; 24 | use typeql_lang::parse_query; 25 | 26 | use crate::{behaviour::Context, generic_step_impl}; 27 | 28 | generic_step_impl! { 29 | #[step(expr = "typeql define")] 30 | async fn typeql_define(context: &mut Context, step: &Step) { 31 | let parsed = parse_query(step.docstring().unwrap()); 32 | assert!(parsed.is_ok()); 33 | let res = context.transaction().query().define(&parsed.unwrap().to_string()).await; 34 | assert!(res.is_ok()); 35 | } 36 | 37 | #[step(expr = "typeql define; throws exception")] 38 | async fn typeql_define_throws(context: &mut Context, step: &Step) { 39 | let parsed = parse_query(step.docstring().unwrap()); 40 | if parsed.is_ok() { 41 | let res = context.transaction().query().define(&parsed.unwrap().to_string()).await; 42 | assert!(res.is_err()); 43 | } 44 | } 45 | 46 | #[step(expr = "typeql define; throws exception containing {string}")] 47 | async fn typeql_define_throws_exception(context: &mut Context, step: &Step, exception: String) { 48 | let result = async { parse_query(step.docstring().unwrap()).map_err(|error| error.to_string()) } 49 | .and_then(|parsed| async move { 50 | context.transaction().query().define(&parsed.to_string()).await.map_err(|error| error.to_string()) 51 | }) 52 | .await; 53 | assert!(result.is_err()); 54 | assert!(result.unwrap_err().contains(&exception)); 55 | } 56 | 57 | #[step(expr = "typeql insert")] 58 | async fn typeql_insert(context: &mut Context, step: &Step) { 59 | let parsed = parse_query(step.docstring().unwrap()); 60 | assert!(parsed.is_ok()); 61 | let inserted = context.transaction().query().insert(&parsed.unwrap().to_string()); 62 | assert!(inserted.is_ok()); 63 | let res = inserted.unwrap().try_collect::>().await; 64 | assert!(res.is_ok()); 65 | } 66 | 67 | #[step(expr = "typeql insert; throws exception")] 68 | async fn typeql_insert_throws(context: &mut Context, step: &Step) { 69 | let parsed = parse_query(step.docstring().unwrap()); 70 | if parsed.is_ok() { 71 | let inserted = context.transaction().query().insert(&parsed.unwrap().to_string()); 72 | if inserted.is_ok() { 73 | let res = inserted.unwrap().try_collect::>().await; 74 | assert!(res.is_err()); 75 | } 76 | } 77 | } 78 | 79 | #[step(expr = "typeql insert; throws exception containing {string}")] 80 | async fn typeql_insert_throws_exception(context: &mut Context, step: &Step, exception: String) { 81 | let result = async { 82 | parse_query(step.docstring().unwrap()).map_err(|error| error.to_string()).and_then(|parsed| { 83 | context.transaction().query().insert(&parsed.to_string()).map_err(|error| error.to_string()) 84 | }) 85 | } 86 | .and_then(|inserted| async { inserted.try_collect::>().await.map_err(|error| error.to_string()) }) 87 | .await; 88 | assert!(result.is_err()); 89 | assert!(result.unwrap_err().contains(&exception)); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tests/behaviour/util.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use cucumber::gherkin::Step; 23 | 24 | pub fn iter_table(step: &Step) -> impl Iterator { 25 | step.table().unwrap().rows.iter().flatten().map(String::as_str) 26 | } 27 | -------------------------------------------------------------------------------- /tests/integration/common.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use std::path::PathBuf; 23 | 24 | use futures::TryFutureExt; 25 | use typedb_client::{ 26 | Connection, Credential, Database, DatabaseManager, Session, SessionType::Schema, TransactionType::Write, 27 | }; 28 | 29 | pub const TEST_DATABASE: &str = "test"; 30 | 31 | pub fn new_core_connection() -> typedb_client::Result { 32 | Connection::new_plaintext("127.0.0.1:1729") 33 | } 34 | 35 | pub fn new_cluster_connection() -> typedb_client::Result { 36 | Connection::new_encrypted( 37 | &["localhost:11729", "localhost:21729", "localhost:31729"], 38 | Credential::with_tls( 39 | "admin", 40 | "password", 41 | Some(&PathBuf::from( 42 | std::env::var("ROOT_CA") 43 | .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), 44 | )), 45 | )?, 46 | ) 47 | } 48 | 49 | pub async fn create_test_database_with_schema(connection: Connection, schema: &str) -> typedb_client::Result { 50 | let databases = DatabaseManager::new(connection); 51 | if databases.contains(TEST_DATABASE).await? { 52 | databases.get(TEST_DATABASE).and_then(Database::delete).await?; 53 | } 54 | databases.create(TEST_DATABASE).await?; 55 | 56 | let database = databases.get(TEST_DATABASE).await?; 57 | let session = Session::new(database, Schema).await?; 58 | let transaction = session.transaction(Write).await?; 59 | transaction.query().define(schema).await?; 60 | transaction.commit().await?; 61 | Ok(()) 62 | } 63 | -------------------------------------------------------------------------------- /tests/integration/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | mod common; 23 | mod queries; 24 | mod runtimes; 25 | -------------------------------------------------------------------------------- /tests/integration/runtimes.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | use futures::StreamExt; 23 | use serial_test::serial; 24 | use typedb_client::{DatabaseManager, Session, SessionType::Data, TransactionType::Write}; 25 | 26 | use super::common; 27 | 28 | #[test] 29 | #[serial] 30 | fn basic_async_std() { 31 | async_std::task::block_on(async { 32 | let connection = common::new_cluster_connection()?; 33 | common::create_test_database_with_schema(connection.clone(), "define person sub entity;").await?; 34 | let databases = DatabaseManager::new(connection); 35 | assert!(databases.contains(common::TEST_DATABASE).await?); 36 | 37 | let session = Session::new(databases.get(common::TEST_DATABASE).await?, Data).await?; 38 | let transaction = session.transaction(Write).await?; 39 | let answer_stream = transaction.query().match_("match $x sub thing;")?; 40 | let results: Vec<_> = answer_stream.collect().await; 41 | transaction.commit().await?; 42 | assert_eq!(results.len(), 5); 43 | assert!(results.into_iter().all(|res| res.is_ok())); 44 | Ok::<(), typedb_client::Error>(()) 45 | }) 46 | .unwrap(); 47 | } 48 | 49 | #[test] 50 | #[serial] 51 | fn basic_smol() { 52 | smol::block_on(async { 53 | let connection = common::new_cluster_connection()?; 54 | common::create_test_database_with_schema(connection.clone(), "define person sub entity;").await?; 55 | let databases = DatabaseManager::new(connection); 56 | assert!(databases.contains(common::TEST_DATABASE).await?); 57 | 58 | let session = Session::new(databases.get(common::TEST_DATABASE).await?, Data).await?; 59 | let transaction = session.transaction(Write).await?; 60 | let answer_stream = transaction.query().match_("match $x sub thing;")?; 61 | let results: Vec<_> = answer_stream.collect().await; 62 | transaction.commit().await?; 63 | assert_eq!(results.len(), 5); 64 | assert!(results.into_iter().all(|res| res.is_ok())); 65 | Ok::<(), typedb_client::Error>(()) 66 | }) 67 | .unwrap(); 68 | } 69 | 70 | #[test] 71 | #[serial] 72 | fn basic_futures() { 73 | futures::executor::block_on(async { 74 | let connection = common::new_cluster_connection()?; 75 | common::create_test_database_with_schema(connection.clone(), "define person sub entity;").await?; 76 | let databases = DatabaseManager::new(connection); 77 | assert!(databases.contains(common::TEST_DATABASE).await?); 78 | 79 | let session = Session::new(databases.get(common::TEST_DATABASE).await?, Data).await?; 80 | let transaction = session.transaction(Write).await?; 81 | let answer_stream = transaction.query().match_("match $x sub thing;")?; 82 | let results: Vec<_> = answer_stream.collect().await; 83 | transaction.commit().await?; 84 | assert_eq!(results.len(), 5); 85 | assert!(results.into_iter().all(|res| res.is_ok())); 86 | Ok::<(), typedb_client::Error>(()) 87 | }) 88 | .unwrap(); 89 | } 90 | -------------------------------------------------------------------------------- /tests/tests.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Vaticle 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | mod behaviour; 23 | mod integration; 24 | -------------------------------------------------------------------------------- /tools/start-cluster-servers.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright (C) 2022 Vaticle 4 | # 5 | # Licensed to the Apache Software Foundation (ASF) under one 6 | # or more contributor license agreements. See the NOTICE file 7 | # distributed with this work for additional information 8 | # regarding copyright ownership. The ASF licenses this file 9 | # to you under the Apache License, Version 2.0 (the 10 | # "License"); you may not use this file except in compliance 11 | # with the License. You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, 16 | # software distributed under the License is distributed on an 17 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | # KIND, either express or implied. See the License for the 19 | # specific language governing permissions and limitations 20 | # under the License. 21 | # 22 | 23 | set -e 24 | 25 | function server_start() { 26 | ./${1}/typedb cluster \ 27 | --storage.data=server/data \ 28 | --server.address=localhost:${1}1729 \ 29 | --server.internal-address.zeromq=localhost:${1}1730 \ 30 | --server.internal-address.grpc=localhost:${1}1731 \ 31 | --server.peers.peer-1.address=localhost:11729 \ 32 | --server.peers.peer-1.internal-address.zeromq=localhost:11730 \ 33 | --server.peers.peer-1.internal-address.grpc=localhost:11731 \ 34 | --server.peers.peer-2.address=localhost:21729 \ 35 | --server.peers.peer-2.internal-address.zeromq=localhost:21730 \ 36 | --server.peers.peer-2.internal-address.grpc=localhost:21731 \ 37 | --server.peers.peer-3.address=localhost:31729 \ 38 | --server.peers.peer-3.internal-address.zeromq=localhost:31730 \ 39 | --server.peers.peer-3.internal-address.grpc=localhost:31731 \ 40 | --server.encryption.enable=true 41 | } 42 | 43 | rm -rf 1 2 3 typedb-cluster-all 44 | 45 | bazel run //tests:typedb-cluster-extractor -- typedb-cluster-all 46 | echo Successfully unarchived TypeDB distribution. Creating 3 copies. 47 | cp -r typedb-cluster-all 1 && cp -r typedb-cluster-all 2 && cp -r typedb-cluster-all 3 48 | echo Starting a cluster consisting of 3 servers... 49 | server_start 1 & 50 | server_start 2 & 51 | server_start 3 & 52 | 53 | ROOT_CA=`realpath typedb-cluster-all/server/conf/encryption/ext-root-ca.pem` 54 | export ROOT_CA 55 | 56 | POLL_INTERVAL_SECS=0.5 57 | MAX_RETRIES=60 58 | RETRY_NUM=0 59 | while [[ $RETRY_NUM -lt $MAX_RETRIES ]]; do 60 | RETRY_NUM=$(($RETRY_NUM + 1)) 61 | if [[ $(($RETRY_NUM % 4)) -eq 0 ]]; then 62 | echo Waiting for TypeDB Cluster servers to start \($(($RETRY_NUM / 2))s\)... 63 | fi 64 | lsof -i :11729 && STARTED1=1 || STARTED1=0 65 | lsof -i :21729 && STARTED2=1 || STARTED2=0 66 | lsof -i :31729 && STARTED3=1 || STARTED3=0 67 | if [[ $STARTED1 -eq 1 && $STARTED2 -eq 1 && $STARTED3 -eq 1 ]]; then 68 | break 69 | fi 70 | sleep $POLL_INTERVAL_SECS 71 | done 72 | if [[ $STARTED1 -eq 0 || $STARTED2 -eq 0 || $STARTED3 -eq 0 ]]; then 73 | echo Failed to start one or more TypeDB Cluster servers 74 | exit 1 75 | fi 76 | sleep 10 77 | echo 3 TypeDB Cluster database servers started 78 | -------------------------------------------------------------------------------- /tools/start-core-server.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright (C) 2022 Vaticle 4 | # 5 | # Licensed to the Apache Software Foundation (ASF) under one 6 | # or more contributor license agreements. See the NOTICE file 7 | # distributed with this work for additional information 8 | # regarding copyright ownership. The ASF licenses this file 9 | # to you under the Apache License, Version 2.0 (the 10 | # "License"); you may not use this file except in compliance 11 | # with the License. You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, 16 | # software distributed under the License is distributed on an 17 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | # KIND, either express or implied. See the License for the 19 | # specific language governing permissions and limitations 20 | # under the License. 21 | # 22 | 23 | set -e 24 | 25 | rm -rf typedb-all 26 | 27 | bazel run //tests:typedb-extractor -- typedb-all 28 | ./typedb-all/typedb server & 29 | sleep 10 30 | -------------------------------------------------------------------------------- /tools/stop-cluster-servers.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright (C) 2022 Vaticle 4 | # 5 | # Licensed to the Apache Software Foundation (ASF) under one 6 | # or more contributor license agreements. See the NOTICE file 7 | # distributed with this work for additional information 8 | # regarding copyright ownership. The ASF licenses this file 9 | # to you under the Apache License, Version 2.0 (the 10 | # "License"); you may not use this file except in compliance 11 | # with the License. You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, 16 | # software distributed under the License is distributed on an 17 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | # KIND, either express or implied. See the License for the 19 | # specific language governing permissions and limitations 20 | # under the License. 21 | # 22 | 23 | set -e 24 | 25 | procs=$(jps | awk '/TypeDBClusterServer/ {print $1}' | paste -sd " " -) 26 | echo $procs 27 | if [ -n "$procs" ]; then 28 | kill $procs 29 | fi 30 | -------------------------------------------------------------------------------- /tools/stop-core-server.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright (C) 2022 Vaticle 4 | # 5 | # Licensed to the Apache Software Foundation (ASF) under one 6 | # or more contributor license agreements. See the NOTICE file 7 | # distributed with this work for additional information 8 | # regarding copyright ownership. The ASF licenses this file 9 | # to you under the Apache License, Version 2.0 (the 10 | # "License"); you may not use this file except in compliance 11 | # with the License. You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, 16 | # software distributed under the License is distributed on an 17 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | # KIND, either express or implied. See the License for the 19 | # specific language governing permissions and limitations 20 | # under the License. 21 | # 22 | 23 | set -e 24 | 25 | kill $(jps | awk '/TypeDBServer/ {print $1}') 26 | --------------------------------------------------------------------------------