diff --git a/.circleci/config.yml b/.circleci/config.yml index 26b1512b10..a7c5982d2d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -83,6 +83,7 @@ commands: deploy-pip-snapshot-unix: steps: - run: | + ulimit -n 100000 export DEPLOY_PIP_USERNAME=$REPO_VATICLE_USERNAME export DEPLOY_PIP_PASSWORD=$REPO_VATICLE_PASSWORD bazel run --define version=$(git rev-parse HEAD) //python:deploy-pip39 -- snapshot @@ -186,6 +187,7 @@ commands: deploy-maven-jni-snapshot-unix: steps: - run: | + ulimit -n 100000 export DEPLOY_MAVEN_USERNAME=$REPO_VATICLE_USERNAME export DEPLOY_MAVEN_PASSWORD=$REPO_VATICLE_PASSWORD bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh @@ -253,6 +255,7 @@ commands: deploy-maven-jni-release-unix: steps: - run: | + ulimit -n 100000 export DEPLOY_MAVEN_USERNAME=$REPO_VATICLE_USERNAME export DEPLOY_MAVEN_PASSWORD=$REPO_VATICLE_PASSWORD bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.md b/.github/ISSUE_TEMPLATE/BUG_REPORT.md index a3d392b1fd..4c32109748 100644 --- a/.github/ISSUE_TEMPLATE/BUG_REPORT.md +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.md @@ -1,38 +1,42 @@ --- name: Bug Report about: Report a bug here, or visit forum.typedb.com for troubleshooting discussions +title: '' labels: bug ---- +assignees: '' -Please replace every line in curly brackets { like this } with an appropriate description, and remove this line. +--- ## Description -{ Please provide a clear and concise description of the bug. } ## Environment -1. OS (where TypeDB Driver runs): { e.g. MacOS 10, Windows 10, Ubuntu 16.4, etc. } -2. TypeDB version (and platform): { e.g. TypeDB 2.11.1, or TypeDB Enterprise 2.11.1 on Google Cloud } -3. TypeDB Driver version: { e.g. typedb-driver 2.11.1 } -4. Other environment details: +1. TypeDB distribution: Core/Enterprise/Cloud +2. TypeDB version: +3. Environment: Linux/Mac/Windows/TypeDB Cloud/Google Cloud/AWS/Azure +4. Studio version: +5. Other details: ## Reproducible Steps -Steps to create the smallest reproducible scenario: -1. { e.g. Run ... } -2. { e.g. Load ... } -3. { e.g. Query ... } -4. { e.g. See error ... } +1. Set up + + +2. Execute + -## Expected Output +3. Test/Query -{ Please describe what you expected to happen. } -## Actual Output +4. Unexpected result + + + +## Expected result + -{ Please describe what actually happened. } - ## Additional information -{ Any additional information, including logs or screenshots if you have any. } +Relevant logs from TypeDB or Driver: + diff --git a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md index 318f156ba2..6c3523a603 100644 --- a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md +++ b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md @@ -1,23 +1,20 @@ --- name: Feature Request about: Request a feature here, or visit forum.typedb.com for ideas and questions +title: '' labels: feature ---- +assignees: '' -Please replace every line in curly brackets { like this } with an appropriate description, and remove this line. +--- ## Problem to Solve -{ Please describe the problem you would like to solve. } ## Current Workaround -{ Please describe how you currently solve or work around this problem, given TypeDB's limitation. } ## Proposed Solution -{ Please describe the solution you would like TypeDB to provide, to solve the problem above. } ## Additional Information -{ Any additional information, including logs or screenshots if you have any. } diff --git a/.github/ISSUE_TEMPLATE/REFACTOR.md b/.github/ISSUE_TEMPLATE/REFACTOR.md index b454bc7ea5..4df9731b66 100644 --- a/.github/ISSUE_TEMPLATE/REFACTOR.md +++ b/.github/ISSUE_TEMPLATE/REFACTOR.md @@ -1,15 +1,15 @@ --- name: Refactor about: Propose an architecture refactor here +title: '' labels: refactor +assignees: '' + --- -Please replace every line in curly brackets { like this } with appropriate answers, and remove this line. ## Problem to Solve -{ Please describe the problem with the current architecture that you would like to solve. } ## Proposed Solution -{ Please describe how you would like to change the architecture, to solve the problem above. } diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index a2ae434d0c..8336a3d105 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,17 +1,4 @@ -**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**. +## Release notes: usage and product changes -## What is the goal of this PR? -{ 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: - - describe the impact of the change in this PR to the _user_ of this repository (e.g. end user, contributor, developer). - - describe the new product behaviour in _present tense_, and the old behaviour and how it's been changed in _past tense_. - - Use the _Royal We_: _"We"_ made changes, not _"I"_ made changes. } - -## What are the changes implemented in this PR? - -{ 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: - - its responsibility, - - how it's expected to behave, and - - how it relates to the adjacent methods/classes/packages it interacts with. - -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'. } +## Implementation diff --git a/README.md b/README.md index f89978e5ba..ce95c5adf2 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ See the table below for links to README files, documentation, and source code. | Driver | Readme | Documentation | Driver location | |---------|--------|-------------------------------------------------------------------------------|-------------------------------------------------------------------------------| -| Rust | [README](https://github.com/vaticle/typedb-driver/tree/development/rust/README.md) | [Documentation](https://typedb.com/docs/clients/2.x/rust/rust-overview) | [`rust/`](https://github.com/vaticle/typedb-driver/tree/development/rust) | -| Python | [README](https://github.com/vaticle/typedb-driver/tree/development/python/README.md) | [Documentation](https://typedb.com/docs/clients/2.x/python/python-overview) | [`python/`](https://github.com/vaticle/typedb-driver/tree/development/python) | -| Node.js | [README](https://github.com/vaticle/typedb-driver/tree/development/nodejs/README.md) | [Documentation](https://typedb.com/docs/clients/2.x/node-js/node-js-overview) | [`nodejs/`](https://github.com/vaticle/typedb-driver/tree/development/nodejs) | -| Java | [README](https://github.com/vaticle/typedb-driver/tree/development/java/README.md) | [Documentation](https://typedb.com/docs/clients/2.x/java/java-overview) | [`java/`](https://github.com/vaticle/typedb-driver/tree/development/java) | +| Rust | [README](https://github.com/vaticle/typedb-driver/tree/development/rust/README.md) | [Documentation](https://typedb.com/docs/clients/rust-driver) | [`rust/`](https://github.com/vaticle/typedb-driver/tree/development/rust) | +| Python | [README](https://github.com/vaticle/typedb-driver/tree/development/python/README.md) | [Documentation](https://typedb.com/docs/clients/python-driver) | [`python/`](https://github.com/vaticle/typedb-driver/tree/development/python) | +| Node.js | [README](https://github.com/vaticle/typedb-driver/tree/development/nodejs/README.md) | [Documentation](https://typedb.com/docs/clients/nodejs-driver) | [`nodejs/`](https://github.com/vaticle/typedb-driver/tree/development/nodejs) | +| Java | [README](https://github.com/vaticle/typedb-driver/tree/development/java/README.md) | [Documentation](https://typedb.com/docs/clients/java-driver) | [`java/`](https://github.com/vaticle/typedb-driver/tree/development/java) | | C | Coming soon | Coming soon | [`c/`](https://github.com/vaticle/typedb-driver/tree/development/c) | diff --git a/RELEASE_NOTES_LATEST.md b/RELEASE_NOTES_LATEST.md index b3de4e4905..301c0afd9b 100644 --- a/RELEASE_NOTES_LATEST.md +++ b/RELEASE_NOTES_LATEST.md @@ -9,7 +9,7 @@ Documentation: https://typedb.com/docs/clients/rust-driver ```sh -cargo add typedb-driver@2.25.5 +cargo add typedb-driver@2.25.7 ``` @@ -29,7 +29,7 @@ Documentation: https://typedb.com/docs/clients/java-driver com.vaticle.typedb typedb-driver - 2.25.5 + 2.25.7 ``` @@ -42,7 +42,7 @@ Documentation: https://typedb.com/docs/clients/python-driver Available through https://pypi.org ``` -pip install typedb-driver==2.25.5 +pip install typedb-driver==2.25.7 ``` ### NodeJS driver @@ -51,25 +51,50 @@ NPM package: https://www.npmjs.com/package/typedb-driver Documentation: https://typedb.com/docs/clients/nodejs-driver ``` -npm install typedb-driver@2.25.5 +npm install typedb-driver@2.25.7 ``` ## New Features -- **Node driver package automatic version stamping** + + +## Bugs Fixed +- **Add untyped value getter for Java values and fix Python type hints** - We leverage Bazel's built-in workspace status and stamping capabilities to ensure that the version of TypeDB Protocol depended on by the node package doesn't go out of sync with the bazel dependency. + We add a simple untyped API to Java's `Value` concepts, which return the value inside of the Value regardless of its type (double/string/etc.). This value is returned as an Object, and useful for equality checks, printing, etc. Additionally, the same API exists in Python and Node already. - To that end, we also add a snapshot deployment test for the node driver, and fix a bug in process of opening a connection to TypeDB Core. + We also fix the Python hints for setting the name of a Type, which was incorrectly hinting the type 'Label' when it should have been a simple string. -## Bugs Fixed +## Code Refactors +- **Silence send errors in network callbacks when receiver dropped** + + Downgrade the "channel closed" `SendError` from ERROR to DEBUG when the receiving end of the stream is dropped before the stream is exhausted. + This used to occur when the network delivered messages to a dropped channel, for example when executing a `match-insert` + and the responses were not consumed explicitly. + + +- **Optimise CI times by retaining server between Java BDD scenarios** + + We optimise Java CI time by not shutting down the TypeDB server between scenarios. Instead, we delete the existing databases each test, which is much faster. + + During this work, we also discovered some sub-par UX in terms of error messages thrown, and missing BDD steps that needed to be implemented. -## Code Refactors ## Other Improvements +- **Fix python BDD TLS connection configuration** + +- **Fix Rust BDD infer flag and python TLS default to false** + +- **Update VERSION and regenerate release notes** + +- **Increase ulimits on unix CircleCI machines** + +- **Update README links to docs** + +- **Simplify github PR and issue templates** -- **Add linker dependencies for windows** + diff --git a/VERSION b/VERSION index 10f67f44f2..7206396da6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.25.5 +2.25.7 diff --git a/dependencies/vaticle/artifacts.bzl b/dependencies/vaticle/artifacts.bzl index 7f477aa7c6..61ee301da8 100644 --- a/dependencies/vaticle/artifacts.bzl +++ b/dependencies/vaticle/artifacts.bzl @@ -29,7 +29,7 @@ def vaticle_typedb_artifact(): artifact_name = "typedb-server-{platform}-{version}.{ext}", tag_source = deployment["artifact.release"], commit_source = deployment["artifact.snapshot"], - commit = "525ac0f5e072242c1d2f360379e9622397497ef2", + tag = "2.25.6", ) def vaticle_typedb_enterprise_artifact(): @@ -39,5 +39,5 @@ def vaticle_typedb_enterprise_artifact(): artifact_name = "typedb-enterprise-all-{platform}-{version}.{ext}", tag_source = deployment_private["artifact.release"], commit_source = deployment_private["artifact.snapshot"], - commit = "fc127f880bf85fab134ddcd1e5409142d47a1c32", + commit = "994220cb44052ba8b66c8e889f94f47ab3b15c0d", ) diff --git a/dependencies/vaticle/repositories.bzl b/dependencies/vaticle/repositories.bzl index 94efabbd37..1f2894ad7d 100644 --- a/dependencies/vaticle/repositories.bzl +++ b/dependencies/vaticle/repositories.bzl @@ -55,5 +55,5 @@ def vaticle_typedb_behaviour(): git_repository( name = "vaticle_typedb_behaviour", remote = "https://github.com/vaticle/typedb-behaviour", - commit = "173c15ec4f15b5f19c2509ecf43f6697efb04247", # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_typedb_behaviour + commit = "b6a1336260a4d11b77750f06abaccba6c9a21f13", # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_typedb_behaviour ) diff --git a/java/README.md b/java/README.md index fce40f1979..37efbad0c9 100644 --- a/java/README.md +++ b/java/README.md @@ -1,10 +1,10 @@ # TypeDB Java Driver ## Driver Architecture -To learn about the mechanism that a TypeDB Driver uses to set up communication with databases running on the TypeDB Server, refer to the [Clients Overview](https://typedb.com/docs/clients/2.x/clients). +To learn about the mechanism that a TypeDB Driver uses to set up communication with databases running on the TypeDB Server, refer to the [Clients Overview](https://typedb.com/docs/clients/overview). ## API Reference -To learn about the methods available for executing queries and retrieving their answers using Driver Java, refer to the [API Reference](https://typedb.com/docs/clients/2.x/java/java-api-ref). +To learn about the methods available for executing queries and retrieving their answers using Driver Java, refer to the [API Reference](https://typedb.com/docs/clients/java-driver/api-reference). ## Import TypeDB Driver for Java through Maven @@ -25,7 +25,7 @@ To learn about the methods available for executing queries and retrieving their ``` -Further documentation: https://typedb.com/docs/clients/2.x/java/java-overview +Further documentation: https://typedb.com/docs/clients/java-driver ## Build TypeDB Driver for Java from Source diff --git a/java/api/concept/value/Value.java b/java/api/concept/value/Value.java index 72111bb110..00ce6d74dd 100644 --- a/java/api/concept/value/Value.java +++ b/java/api/concept/value/Value.java @@ -114,6 +114,17 @@ default Value asValue() { */ boolean isDateTime(); + /** + * Returns an untyped Object value of this value concept. + * This is useful for value equality or printing without having to switch on the actual contained value. + * + *

Examples

+ *
+     * value.asUntyped();
+     * 
+ */ + Object asUntyped(); + /** * Returns a boolean value of this value concept. * If the value has another type, raises an exception. diff --git a/java/concept/value/ValueImpl.java b/java/concept/value/ValueImpl.java index 2e1c900318..23cfc37bc2 100644 --- a/java/concept/value/ValueImpl.java +++ b/java/concept/value/ValueImpl.java @@ -113,6 +113,16 @@ public boolean isDateTime() { return value_is_date_time(nativeObject); } + @Override + public Object asUntyped() { + if (isBoolean()) return asBoolean(); + else if (isLong()) return asLong(); + else if (isDouble()) return asDouble(); + else if (isString()) return asString(); + else if (isDateTime()) return asDateTime(); + throw new TypeDBDriverException(UNEXPECTED_NATIVE_VALUE); + } + @Override public boolean asBoolean() { if (!isBoolean()) throw new TypeDBDriverException(ILLEGAL_CAST, "boolean"); @@ -160,11 +170,6 @@ public int hashCode() { } private int computeHash() { - if (isBoolean()) return Boolean.hashCode(asBoolean()); - else if (isLong()) return Long.hashCode(asLong()); - else if (isDouble()) return Double.hashCode(asDouble()); - else if (isString()) return asString().hashCode(); - else if (isDateTime()) return asDateTime().hashCode(); - return -1; + return asUntyped().hashCode(); } } diff --git a/java/connection/TypeDBDriverImpl.java b/java/connection/TypeDBDriverImpl.java index ef3e46f06b..e43cad945f 100644 --- a/java/connection/TypeDBDriverImpl.java +++ b/java/connection/TypeDBDriverImpl.java @@ -105,6 +105,7 @@ public TypeDBSession session(String database, TypeDBSession.Type type, TypeDBOpt @Override public void close() { + if (!isOpen()) return; try { connection_force_close(nativeObject); } catch (com.vaticle.typedb.driver.jni.Error error) { diff --git a/java/docs/data/Value.adoc b/java/docs/data/Value.adoc index f7b7175c03..eceb645450 100644 --- a/java/docs/data/Value.adoc +++ b/java/docs/data/Value.adoc @@ -349,6 +349,28 @@ Casts the concept to ``Type``. concept.asType(); ---- +[#_Value_asUntyped_] +==== asUntyped + +[source,java] +---- +java.lang.Object asUntyped() +---- + +Returns an untyped ``Object`` value of this value concept. This is useful for value equality or printing without having to switch on the actual contained value. + + +[caption=""] +.Returns +`java.lang.Object` + +[caption=""] +.Code examples +[source,java] +---- +value.asUntyped(); +---- + [#_Value_asValue_] ==== asValue diff --git a/java/test/behaviour/concept/thing/attribute/AttributeTest.java b/java/test/behaviour/concept/thing/attribute/AttributeTest.java index 125865a708..794a9a1caf 100644 --- a/java/test/behaviour/concept/thing/attribute/AttributeTest.java +++ b/java/test/behaviour/concept/thing/attribute/AttributeTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/concept/thing/attribute.feature", - tags = "not @ignore and not @ignore-typedb" + tags = "not @ignore and not @ignore-typedb-driver and not @ignore-typedb-driver-java" ) public class AttributeTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/concept/thing/entity/EntityTest.java b/java/test/behaviour/concept/thing/entity/EntityTest.java index f3fdf0e993..159b9bfbae 100644 --- a/java/test/behaviour/concept/thing/entity/EntityTest.java +++ b/java/test/behaviour/concept/thing/entity/EntityTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/concept/thing/entity.feature", - tags = "not @ignore and not @ignore-typedb" + tags = "not @ignore and not @ignore-typedb-driver and not @ignore-typedb-driver-java" ) public class EntityTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/concept/thing/relation/RelationTest.java b/java/test/behaviour/concept/thing/relation/RelationTest.java index a53f7ea58a..319c1bf0e5 100644 --- a/java/test/behaviour/concept/thing/relation/RelationTest.java +++ b/java/test/behaviour/concept/thing/relation/RelationTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/concept/thing/relation.feature", - tags = "not @ignore and not @ignore-typedb" + tags = "not @ignore and not @ignore-typedb-driver and not @ignore-typedb-driver-java" ) public class RelationTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/concept/type/attributetype/AttributeTypeTest.java b/java/test/behaviour/concept/type/attributetype/AttributeTypeTest.java index 0a4d9e0265..ca9dccb401 100644 --- a/java/test/behaviour/concept/type/attributetype/AttributeTypeTest.java +++ b/java/test/behaviour/concept/type/attributetype/AttributeTypeTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/concept/type/attributetype.feature", - tags = "not @ignore and not @ignore-typedb" + tags = "not @ignore and not @ignore-typedb-driver and not @ignore-typedb-driver-java" ) public class AttributeTypeTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/concept/type/entitytype/EntityTypeTest.java b/java/test/behaviour/concept/type/entitytype/EntityTypeTest.java index 2d18b9b582..208f01c19e 100644 --- a/java/test/behaviour/concept/type/entitytype/EntityTypeTest.java +++ b/java/test/behaviour/concept/type/entitytype/EntityTypeTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/concept/type/entitytype.feature", - tags = "not @ignore and not @ignore-typedb" + tags = "not @ignore and not @ignore-typedb-driver and not @ignore-typedb-driver-java" ) public class EntityTypeTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/concept/type/relationtype/RelationTypeTest.java b/java/test/behaviour/concept/type/relationtype/RelationTypeTest.java index 1ee6710d11..0d505b9c1d 100644 --- a/java/test/behaviour/concept/type/relationtype/RelationTypeTest.java +++ b/java/test/behaviour/concept/type/relationtype/RelationTypeTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/concept/type/relationtype.feature", - tags = "not @ignore and not @ignore-typedb" + tags = "not @ignore and not @ignore-typedb-driver and not @ignore-typedb-driver-java" ) public class RelationTypeTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/connection/ConnectionStepsBase.java b/java/test/behaviour/connection/ConnectionStepsBase.java index 353d0fe590..c79d8c8738 100644 --- a/java/test/behaviour/connection/ConnectionStepsBase.java +++ b/java/test/behaviour/connection/ConnectionStepsBase.java @@ -26,6 +26,7 @@ import com.vaticle.typedb.driver.api.TypeDBSession; import com.vaticle.typedb.driver.api.TypeDBTransaction; import com.vaticle.typedb.common.test.TypeDBSingleton; +import com.vaticle.typedb.driver.api.database.Database; import java.util.ArrayList; import java.util.HashMap; @@ -79,18 +80,11 @@ void before() { } sessionOptions = createOptions().infer(true); transactionOptions = createOptions().infer(true); - TypeDBSingleton.resetTypeDBRunner(); System.out.println("ConnectionSteps.before"); } void after() { - // TODO: Remove this once the server segfault issue is fixed (typedb#6135) - try { - Thread.sleep(10); - } catch (InterruptedException e) { - e.printStackTrace(); - } sessions.parallelStream().forEach(TypeDBSession::close); sessions.clear(); @@ -104,11 +98,9 @@ void after() { sessionsToTransactions.clear(); sessionsToTransactionsParallel.clear(); sessionsParallelToTransactionsParallel.clear(); - - TypeDBSingleton.resetTypeDBRunner(); - - // This will segfault if a double free happens in finalize() for whatever reason. - System.gc(); + driver = createTypeDBDriver(TypeDBSingleton.getTypeDBRunner().address()); + driver.databases().all().forEach(Database::delete); + driver.close(); System.out.println("ConnectionSteps.after"); } diff --git a/java/test/behaviour/connection/ConnectionStepsCore.java b/java/test/behaviour/connection/ConnectionStepsCore.java index 31c7d2c6cd..ed51b2b8a2 100644 --- a/java/test/behaviour/connection/ConnectionStepsCore.java +++ b/java/test/behaviour/connection/ConnectionStepsCore.java @@ -21,12 +21,12 @@ package com.vaticle.typedb.driver.test.behaviour.connection; -import com.vaticle.typedb.driver.TypeDB; -import com.vaticle.typedb.driver.api.TypeDBDriver; -import com.vaticle.typedb.driver.api.TypeDBOptions; import com.vaticle.typedb.common.test.TypeDBRunner; import com.vaticle.typedb.common.test.TypeDBSingleton; import com.vaticle.typedb.common.test.core.TypeDBCoreRunner; +import com.vaticle.typedb.driver.TypeDB; +import com.vaticle.typedb.driver.api.TypeDBDriver; +import com.vaticle.typedb.driver.api.TypeDBOptions; import io.cucumber.java.After; import io.cucumber.java.Before; import io.cucumber.java.en.Given; @@ -36,6 +36,13 @@ public class ConnectionStepsCore extends ConnectionStepsBase { @Override public void beforeAll() { super.beforeAll(); + try { + TypeDBCoreRunner typeDBCoreRunner = new TypeDBCoreRunner(); + TypeDBSingleton.setTypeDBRunner(typeDBCoreRunner); + typeDBCoreRunner.start(); + } catch (InterruptedException | java.util.concurrent.TimeoutException | java.io.IOException e) { + e.printStackTrace(); + } } @Before @@ -63,22 +70,9 @@ public void typedb_starts() { TypeDBRunner runner = TypeDBSingleton.getTypeDBRunner(); if (runner != null && runner.isStopped()) { runner.start(); - } else { - try { - TypeDBCoreRunner typeDBCoreRunner = new TypeDBCoreRunner(); - TypeDBSingleton.setTypeDBRunner(typeDBCoreRunner); - typeDBCoreRunner.start(); - } catch (InterruptedException | java.util.concurrent.TimeoutException | java.io.IOException e) { - e.printStackTrace(); - } } } - @When("typedb stops") - public void typedb_stops() { - TypeDBSingleton.getTypeDBRunner().stop(); - } - @Override @When("connection opens with default authentication") public void connection_opens_with_default_authentication() { diff --git a/java/test/behaviour/connection/ConnectionStepsEnterprise.java b/java/test/behaviour/connection/ConnectionStepsEnterprise.java index 55eedfb367..9dfa181141 100644 --- a/java/test/behaviour/connection/ConnectionStepsEnterprise.java +++ b/java/test/behaviour/connection/ConnectionStepsEnterprise.java @@ -28,6 +28,7 @@ import com.vaticle.typedb.common.test.TypeDBRunner; import com.vaticle.typedb.common.test.TypeDBSingleton; import com.vaticle.typedb.common.test.enterprise.TypeDBEnterpriseRunner; +import com.vaticle.typedb.driver.api.database.Database; import io.cucumber.java.After; import io.cucumber.java.Before; import io.cucumber.java.en.Given; @@ -44,6 +45,9 @@ public class ConnectionStepsEnterprise extends ConnectionStepsBase { @Override public void beforeAll() { super.beforeAll(); + TypeDBEnterpriseRunner enterpriseRunner = TypeDBEnterpriseRunner.create(Paths.get("."), 1); + TypeDBSingleton.setTypeDBRunner(enterpriseRunner); + enterpriseRunner.start(); } @Before @@ -54,6 +58,19 @@ public synchronized void before() { @After public synchronized void after() { super.after(); + driver = createTypeDBDriver(TypeDBSingleton.getTypeDBRunner().address()); + driver.users().all().forEach(user -> { + if (!user.username().equals("admin")) { + driver.users().delete(user.username()); + } + }); + driver.close(); + try { + // sleep for eventual consistency to catch up with database deletion on all servers + Thread.sleep(100); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } } @Override @@ -97,6 +114,12 @@ public void connection_has_been_opened() { super.connection_has_been_opened(); } + @Override + @Given("connection does not have any database") + public void connection_does_not_have_any_database() { + super.connection_does_not_have_any_database(); + } + @Override @When("connection closes") public void connection_closes() { @@ -105,13 +128,7 @@ public void connection_closes() { @Given("typedb has configuration") public void typedb_has_configuration(Map map) { - TypeDBSingleton.deleteTypeDBRunner(); - Map serverOpts = new HashMap<>(); - for (Map.Entry entry : map.entrySet()) { - serverOpts.put("--" + entry.getKey(), entry.getValue()); - } - TypeDBEnterpriseRunner enterpriseRunner = TypeDBEnterpriseRunner.create(Paths.get("."), 1, serverOpts); - TypeDBSingleton.setTypeDBRunner(enterpriseRunner); + // no-op: configuration tests are only run on the backend themselves } @When("typedb starts") @@ -119,22 +136,6 @@ public void typedb_starts() { TypeDBRunner runner = TypeDBSingleton.getTypeDBRunner(); if (runner != null && runner.isStopped()) { runner.start(); - } else { - TypeDBEnterpriseRunner enterpriseRunner = TypeDBEnterpriseRunner.create(Paths.get("."), 1); - TypeDBSingleton.setTypeDBRunner(enterpriseRunner); - enterpriseRunner.start(); } } - - @When("typedb stops") - public void typedb_stops() { - TypeDBSingleton.getTypeDBRunner().stop(); - } - - - @Override - @Given("connection does not have any database") - public void connection_does_not_have_any_database() { - super.connection_does_not_have_any_database(); - } } diff --git a/java/test/behaviour/connection/database/DatabaseTestCore.java b/java/test/behaviour/connection/database/DatabaseTestCore.java index 252d0511e8..98e5919e78 100644 --- a/java/test/behaviour/connection/database/DatabaseTestCore.java +++ b/java/test/behaviour/connection/database/DatabaseTestCore.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/connection/database.feature", - tags = "not @ignore and not @ignore-driver-java and not @ignore-typedb-driver-java" + tags = "not @ignore and not @ignore-typedb-driver and not @ignore-typedb-driver-java" ) public class DatabaseTestCore extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/connection/database/DatabaseTestEnterprise.java b/java/test/behaviour/connection/database/DatabaseTestEnterprise.java index 83270ee6dd..e233ebb790 100644 --- a/java/test/behaviour/connection/database/DatabaseTestEnterprise.java +++ b/java/test/behaviour/connection/database/DatabaseTestEnterprise.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/connection/database.feature", - tags = "not @ignore and not @ignore-driver-java and not @ignore-typedb-enterprise-driver-java" + tags = "not @ignore and not @ignore-typedb-driver and not @ignore-typedb-driver-java" ) public class DatabaseTestEnterprise extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/connection/session/SessionTest.java b/java/test/behaviour/connection/session/SessionTest.java index c064745f35..5fc9b2acfa 100644 --- a/java/test/behaviour/connection/session/SessionTest.java +++ b/java/test/behaviour/connection/session/SessionTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/connection/session.feature", - tags = "not @ignore and not @ignore-typedb-driver-java" + tags = "not @ignore and not @ignore-typedb-driver and not @ignore-typedb-driver-java" ) public class SessionTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/connection/transaction/TransactionTest.java b/java/test/behaviour/connection/transaction/TransactionTest.java index efd60fcb37..d3fa052fe4 100644 --- a/java/test/behaviour/connection/transaction/TransactionTest.java +++ b/java/test/behaviour/connection/transaction/TransactionTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/connection/transaction.feature", - tags = "not @ignore and not @ignore-typedb-driver-java" + tags = "not @ignore and not @ignore-typedb-driver and not @ignore-typedb-driver-java" ) public class TransactionTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/connection/user/UserTestEnterprise.java b/java/test/behaviour/connection/user/UserTestEnterprise.java index 81e252dc81..f5ca13c615 100644 --- a/java/test/behaviour/connection/user/UserTestEnterprise.java +++ b/java/test/behaviour/connection/user/UserTestEnterprise.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/connection/user.feature", - tags = "not @ignore and not @ignore-driver-java and not @ignore-typedb-enterprise-driver-java" + tags = "not @ignore and not @ignore-typedb-driver and not @ignore-typedb-driver-java" ) public class UserTestEnterprise extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/driver/query/QueryTest.java b/java/test/behaviour/driver/query/QueryTest.java index 869c4a5213..0e17ef6a1e 100644 --- a/java/test/behaviour/driver/query/QueryTest.java +++ b/java/test/behaviour/driver/query/QueryTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/driver/query.feature", - tags = "not @ignore and not @ignore-typedb-driver-java" + tags = "not @ignore and not @ignore-typedb-driver and not @ignore-typedb-driver-java" ) public class QueryTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/query/language/define/DefineTest.java b/java/test/behaviour/query/language/define/DefineTest.java index 5ad0f8e7c3..80665f2e18 100644 --- a/java/test/behaviour/query/language/define/DefineTest.java +++ b/java/test/behaviour/query/language/define/DefineTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/query/language/define.feature", - tags = "not @ignore and not @ignore-typedb-driver-java" + tags = "not @ignore and not @ignore-typedb-driver and not @ignore-typedb-driver-java" ) public class DefineTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/query/language/delete/DeleteTest.java b/java/test/behaviour/query/language/delete/DeleteTest.java index 4eaca4bec5..ab7d7e2612 100644 --- a/java/test/behaviour/query/language/delete/DeleteTest.java +++ b/java/test/behaviour/query/language/delete/DeleteTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/query/language/delete.feature", - tags = "not @ignore and not @ignore-typedb-driver-java" + tags = "not @ignore and not @ignore-typedb-driver and not @ignore-typedb-driver-java" ) public class DeleteTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/query/language/expression/ExpressionTest.java b/java/test/behaviour/query/language/expression/ExpressionTest.java index 70e9edd331..d78f7b8ea3 100644 --- a/java/test/behaviour/query/language/expression/ExpressionTest.java +++ b/java/test/behaviour/query/language/expression/ExpressionTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/query/language/expression.feature", - tags = "not @ignore and not @ignore-typedb-driver-java" + tags = "not @ignore and not @ignore-driver and not @ignore-typedb-driver-java" ) public class ExpressionTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/query/language/fetch/FetchTest.java b/java/test/behaviour/query/language/fetch/FetchTest.java index ea8c8ca73e..e7ef288c47 100644 --- a/java/test/behaviour/query/language/fetch/FetchTest.java +++ b/java/test/behaviour/query/language/fetch/FetchTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/query/language/fetch.feature", - tags = "not @ignore and not @ignore-typedb-driver-java" + tags = "not @ignore and not @ignore-driver and not @ignore-typedb-driver-java" ) public class FetchTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/query/language/get/GetTest.java b/java/test/behaviour/query/language/get/GetTest.java index f008c65479..27ac1d84f8 100644 --- a/java/test/behaviour/query/language/get/GetTest.java +++ b/java/test/behaviour/query/language/get/GetTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/query/language/get.feature", - tags = "not @ignore and not @ignore-typedb-driver-java" + tags = "not @ignore and not @ignore-driver and not @ignore-typedb-driver-java" ) public class GetTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/query/language/insert/InsertTest.java b/java/test/behaviour/query/language/insert/InsertTest.java index 4ca2a78b89..137a6d85df 100644 --- a/java/test/behaviour/query/language/insert/InsertTest.java +++ b/java/test/behaviour/query/language/insert/InsertTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/query/language/insert.feature", - tags = "not @ignore and not @ignore-typedb-driver-java" + tags = "not @ignore and not @ignore-driver and not @ignore-typedb-driver-java" ) public class InsertTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/query/language/match/MatchTest.java b/java/test/behaviour/query/language/match/MatchTest.java index 376db8c6b0..119de6def7 100644 --- a/java/test/behaviour/query/language/match/MatchTest.java +++ b/java/test/behaviour/query/language/match/MatchTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/query/language/match.feature", - tags = "not @ignore and not @ignore-typedb-driver-java" + tags = "not @ignore and not @ignore-driver and not @ignore-typedb-driver-java" ) public class MatchTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/query/language/modifiers/ModifiersTest.java b/java/test/behaviour/query/language/modifiers/ModifiersTest.java index cda4d08608..1a128e0fbe 100644 --- a/java/test/behaviour/query/language/modifiers/ModifiersTest.java +++ b/java/test/behaviour/query/language/modifiers/ModifiersTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/query/language/modifiers.feature", - tags = "not @ignore and not @ignore-typedb-driver-java" + tags = "not @ignore and not @ignore-driver and not @ignore-typedb-driver-java" ) public class ModifiersTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/query/language/undefine/UndefineTest.java b/java/test/behaviour/query/language/undefine/UndefineTest.java index b0dbca4442..f3ca8fa0ae 100644 --- a/java/test/behaviour/query/language/undefine/UndefineTest.java +++ b/java/test/behaviour/query/language/undefine/UndefineTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/query/language/undefine.feature", - tags = "not @ignore and not @ignore-typedb-driver-java" + tags = "not @ignore and not @ignore-driver and not @ignore-typedb-driver-java" ) public class UndefineTest extends BehaviourTest { // ATTENTION: diff --git a/java/test/behaviour/query/language/update/UpdateTest.java b/java/test/behaviour/query/language/update/UpdateTest.java index 227d87744b..81c3bdbe70 100644 --- a/java/test/behaviour/query/language/update/UpdateTest.java +++ b/java/test/behaviour/query/language/update/UpdateTest.java @@ -32,7 +32,7 @@ plugin = "pretty", glue = "com.vaticle.typedb.driver.test.behaviour", features = "external/vaticle_typedb_behaviour/query/language/update.feature", - tags = "not @ignore and not @ignore-typedb-driver-java" + tags = "not @ignore and not @ignore-driver and not @ignore-typedb-driver-java" ) public class UpdateTest extends BehaviourTest { // ATTENTION: diff --git a/nodejs/README.md b/nodejs/README.md index b7169467e4..7043b0c636 100644 --- a/nodejs/README.md +++ b/nodejs/README.md @@ -7,10 +7,10 @@ [![Stack Overflow](https://img.shields.io/badge/stackoverflow-typeql-3dce8c.svg)](https://stackoverflow.com/questions/tagged/typeql) ## Driver Architecture -To learn about the mechanism that a TypeDB Driver uses to set up communication with databases running on the TypeDB Server, refer to the [Clients Overview](https://typedb.com/docs/clients/2.x/clients). +To learn about the mechanism that a TypeDB Driver uses to set up communication with databases running on the TypeDB Server, refer to the [Clients Overview](https://typedb.com/docs/clients/overview). ## API Reference -To learn about the methods available for executing queries and retrieving their answers using Driver NodeJS, refer to the [API Reference](https://typedb.com/docs/clients/2.x/node-js/node-js-api-ref). +To learn about the methods available for executing queries and retrieving their answers using Driver NodeJS, refer to the [API Reference](https://typedb.com/docs/clients/nodejs-driver/api-reference). ## Installation @@ -19,7 +19,7 @@ To learn about the methods available for executing queries and retrieving their ```shell script npm install typedb-driver ``` -Further documentation: https://typedb.com/docs/clients/2.x/node-js/node-js-overview +Further documentation: https://typedb.com/docs/clients/nodejs-driver ## Using TypeScript `typedb-driver` is a TypeScript project and provides its own type definitions out of the box - for example: diff --git a/nodejs/common/errors/ErrorMessage.ts b/nodejs/common/errors/ErrorMessage.ts index c06c197b5a..ca5133057f 100644 --- a/nodejs/common/errors/ErrorMessage.ts +++ b/nodejs/common/errors/ErrorMessage.ts @@ -76,7 +76,7 @@ export abstract class ErrorMessage { export namespace ErrorMessage { export class Driver extends ErrorMessage { constructor(code: number, message: (args?: Stringable[]) => string) { - super("CLI", code, "Driver Error", message) + super("DRI", code, "Driver Error", message) } } @@ -94,14 +94,15 @@ export namespace ErrorMessage { export const UNKNOWN_STREAM_STATE = new Driver(11, (args: Stringable[]) => `RPC transaction stream response '${args[0]}' is unknown.`); export const MISSING_RESPONSE = new Driver(12, (args: Stringable[]) => `The required field 'res' of type '${args[0]}' was not set.`); export const UNKNOWN_REQUEST_ID = new Driver(13, (args: Stringable[]) => `Received a response with unknown request id '${args[0]}'.`); - export const ENTPERPRISE_NO_PRIMARY_REPLICA_YET = new Driver(14, (args: Stringable[]) => `No replica has been marked as the primary replica for latest known term '${args[0]}'.`); - export const ENTPERPRISE_UNABLE_TO_CONNECT = new Driver(15, (args: Stringable[]) => `Unable to connect to TypeDB Enterprise. Attempted connecting to the enterprise nodes, but none are available: '${args[1]}'.`); - export const ENTPERPRISE_REPLICA_NOT_PRIMARY = new Driver(16, () => `The replica is not the primary replica.`); - export const ENTPERPRISE_ALL_NODES_FAILED = new Driver(17, (args: Stringable[]) => `Attempted connecting to all enterprise nodes, but the following errors occurred: \n'${args[0]}'`); - export const ENTPERPRISE_USER_DOES_NOT_EXIST = new Driver(18, (args: Stringable[]) => `The user '${args[0]}' does not exist.`); - export const ENTPERPRISE_TOKEN_CREDENTIAL_INVALID = new Driver(19, (args: Stringable[]) => `Invalid token credential.`); - export const ENTERPRISE_INVALID_ROOT_CA_PATH = new Driver(20, (args: Stringable[]) => `The provided Root CA path '${args[0]}' does not exist`); - export const UNRECOGNISED_SESSION_TYPE = new Driver(21, (args: Stringable[]) => `Session type '${args[1]}' was not recognised.`); + export const ENTERPRISE_NO_PRIMARY_REPLICA_YET = new Driver(14, (args: Stringable[]) => `No replica has been marked as the primary replica for latest known term '${args[0]}'.`); + export const ENTERPRISE_UNABLE_TO_CONNECT = new Driver(15, (args: Stringable[]) => `Unable to connect to TypeDB Enterprise. Attempted connecting to the enterprise nodes, but none are available: '${args[1]}'.`); + export const ENTERPRISE_REPLICA_NOT_PRIMARY = new Driver(16, () => `The replica is not the primary replica.`); + export const ENTERPRISE_ALL_NODES_FAILED = new Driver(17, (args: Stringable[]) => `Attempted connecting to all enterprise nodes, but the following errors occurred: \n'${args[0]}'`); + export const USER_MANAGEMENT_ENTERPRISE_ONLY = new Driver(18, () => `User management is only available in TypeDB Enterprise servers.`); + export const ENTERPRISE_USER_DOES_NOT_EXIST = new Driver(19, (args: Stringable[]) => `The user '${args[0]}' does not exist.`); + export const ENTERPRISE_TOKEN_CREDENTIAL_INVALID = new Driver(20, (args: Stringable[]) => `Invalid token credential.`); + export const ENTERPRISE_INVALID_ROOT_CA_PATH = new Driver(21, (args: Stringable[]) => `The provided Root CA path '${args[0]}' does not exist`); + export const UNRECOGNISED_SESSION_TYPE = new Driver(22, (args: Stringable[]) => `Session type '${args[1]}' was not recognised.`); } export class Concept extends ErrorMessage { diff --git a/nodejs/common/errors/TypeDBDriverError.ts b/nodejs/common/errors/TypeDBDriverError.ts index d1dc5a2a41..90f1bdbccd 100644 --- a/nodejs/common/errors/TypeDBDriverError.ts +++ b/nodejs/common/errors/TypeDBDriverError.ts @@ -22,8 +22,8 @@ import {ServiceError} from "@grpc/grpc-js"; import {Status} from "@grpc/grpc-js/build/src/constants"; import {ErrorMessage} from "./ErrorMessage"; -import ENTERPRISE_REPLICA_NOT_PRIMARY = ErrorMessage.Driver.ENTPERPRISE_REPLICA_NOT_PRIMARY; -import ENTERPRISE_TOKEN_CREDENTIAL_INVALID = ErrorMessage.Driver.ENTPERPRISE_TOKEN_CREDENTIAL_INVALID; +import ENTERPRISE_REPLICA_NOT_PRIMARY = ErrorMessage.Driver.ENTERPRISE_REPLICA_NOT_PRIMARY; +import ENTERPRISE_TOKEN_CREDENTIAL_INVALID = ErrorMessage.Driver.ENTERPRISE_TOKEN_CREDENTIAL_INVALID; import UNABLE_TO_CONNECT = ErrorMessage.Driver.UNABLE_TO_CONNECT; import RPC_METHOD_UNAVAILABLE = ErrorMessage.Driver.RPC_METHOD_UNAVAILABLE; diff --git a/nodejs/connection/TypeDBDatabaseImpl.ts b/nodejs/connection/TypeDBDatabaseImpl.ts index fdf834a7fb..13ce3d983e 100644 --- a/nodejs/connection/TypeDBDatabaseImpl.ts +++ b/nodejs/connection/TypeDBDatabaseImpl.ts @@ -30,7 +30,7 @@ import {RequestBuilder} from "../common/rpc/RequestBuilder"; import {ErrorMessage} from "../common/errors/ErrorMessage"; import UNABLE_TO_CONNECT = ErrorMessage.Driver.UNABLE_TO_CONNECT; import DATABASE_DOES_NOT_EXIST = ErrorMessage.Driver.DATABASE_DOES_NOT_EXIST; -import ENTERPRISE_REPLICA_NOT_PRIMARY = ErrorMessage.Driver.ENTPERPRISE_REPLICA_NOT_PRIMARY; +import ENTERPRISE_REPLICA_NOT_PRIMARY = ErrorMessage.Driver.ENTERPRISE_REPLICA_NOT_PRIMARY; const PRIMARY_REPLICA_TASK_MAX_RETRIES = 10; const FETCH_REPLICAS_MAX_RETRIES = 10; diff --git a/nodejs/connection/TypeDBDatabaseManagerImpl.ts b/nodejs/connection/TypeDBDatabaseManagerImpl.ts index 4698d4f2a1..32a0d45688 100644 --- a/nodejs/connection/TypeDBDatabaseManagerImpl.ts +++ b/nodejs/connection/TypeDBDatabaseManagerImpl.ts @@ -26,8 +26,8 @@ import {TypeDBDriverError} from "../common/errors/TypeDBDriverError"; import {RequestBuilder} from "../common/rpc/RequestBuilder"; import {ServerDriver, TypeDBDriverImpl} from "./TypeDBDriverImpl"; import {TypeDBDatabaseImpl} from "./TypeDBDatabaseImpl"; -import ENTERPRISE_ALL_NODES_FAILED = ErrorMessage.Driver.ENTPERPRISE_ALL_NODES_FAILED; -import ENTERPRISE_REPLICA_NOT_PRIMARY = ErrorMessage.Driver.ENTPERPRISE_REPLICA_NOT_PRIMARY; +import ENTERPRISE_ALL_NODES_FAILED = ErrorMessage.Driver.ENTERPRISE_ALL_NODES_FAILED; +import ENTERPRISE_REPLICA_NOT_PRIMARY = ErrorMessage.Driver.ENTERPRISE_REPLICA_NOT_PRIMARY; import DB_DOES_NOT_EXIST = ErrorMessage.Driver.DATABASE_DOES_NOT_EXIST; export class TypeDBDatabaseManagerImpl implements DatabaseManager { diff --git a/nodejs/connection/TypeDBDriverImpl.ts b/nodejs/connection/TypeDBDriverImpl.ts index 0d1001a346..aad3e3f140 100644 --- a/nodejs/connection/TypeDBDriverImpl.ts +++ b/nodejs/connection/TypeDBDriverImpl.ts @@ -35,13 +35,13 @@ import {TypeDBDatabaseManagerImpl} from "./TypeDBDatabaseManagerImpl"; import {TypeDBSessionImpl} from "./TypeDBSessionImpl"; import {TypeDBStubImpl} from "./TypeDBStubImpl"; import DRIVER_NOT_OPEN = ErrorMessage.Driver.DRIVER_NOT_OPEN; -import ENTERPRISE_UNABLE_TO_CONNECT = ErrorMessage.Driver.ENTPERPRISE_UNABLE_TO_CONNECT; +import ENTERPRISE_UNABLE_TO_CONNECT = ErrorMessage.Driver.ENTERPRISE_UNABLE_TO_CONNECT; import SESSION_ID_EXISTS = ErrorMessage.Driver.SESSION_ID_EXISTS; import UNABLE_TO_CONNECT = ErrorMessage.Driver.UNABLE_TO_CONNECT; export class TypeDBDriverImpl implements TypeDBDriver { private _isOpen: boolean; - private _isEnterprise: boolean; + private readonly _isEnterprise: boolean; private readonly _initAddresses: string[]; private readonly _credential: TypeDBCredential; @@ -119,6 +119,10 @@ export class TypeDBDriverImpl implements TypeDBDriver { return this._isOpen; } + isEnterprise(): boolean { + return this._isEnterprise; + } + async session(databaseName: string, type: SessionType, options?: TypeDBOptions): Promise { if (!this.isOpen()) throw new TypeDBDriverError(DRIVER_NOT_OPEN); if (!options) options = new TypeDBOptions(); diff --git a/nodejs/connection/TypeDBStubImpl.ts b/nodejs/connection/TypeDBStubImpl.ts index ae0151103c..4daec24536 100644 --- a/nodejs/connection/TypeDBStubImpl.ts +++ b/nodejs/connection/TypeDBStubImpl.ts @@ -27,7 +27,7 @@ import {TypeDBStub} from "../common/rpc/TypeDBStub"; import {RequestBuilder} from "../common/rpc/RequestBuilder"; import {ErrorMessage} from "../common/errors/ErrorMessage"; import {TypeDBClient as GRPCStub} from "typedb-protocol/proto/typedb-service"; -import ENTERPRISE_TOKEN_CREDENTIAL_INVALID = ErrorMessage.Driver.ENTPERPRISE_TOKEN_CREDENTIAL_INVALID; +import ENTERPRISE_TOKEN_CREDENTIAL_INVALID = ErrorMessage.Driver.ENTERPRISE_TOKEN_CREDENTIAL_INVALID; function isServiceError(e: any): e is ServiceError { return "code" in e; diff --git a/nodejs/test/behaviour/connection/ConnectionStepsBase.ts b/nodejs/test/behaviour/connection/ConnectionStepsBase.ts index 78d30e3eef..be6ede8f54 100644 --- a/nodejs/test/behaviour/connection/ConnectionStepsBase.ts +++ b/nodejs/test/behaviour/connection/ConnectionStepsBase.ts @@ -83,18 +83,23 @@ export async function afterBase() { if (driver.isOpen()) { await driver.close(); } + await createDefaultDriver(); + for (const db of (await driver.databases.all())) { + await db.delete(); + } + await driver.close(); } Given('typedb has configuration', (table: any) => { - // TODO: prepare a configuration through the TypeDB runner once it exists + // empty }) Given('typedb starts', async () => { - // TODO: start TypeDB through the TypeDB runner once it exists + // empty }) Given('typedb stops', async () => { - // TODO: stop TypeDB through the TypeDB runner once it exists + // empty }) Given('connection opens with default authentication', async () => { diff --git a/nodejs/test/behaviour/connection/transaction/BUILD b/nodejs/test/behaviour/connection/transaction/BUILD index eac3fc72c3..08094aa23c 100644 --- a/nodejs/test/behaviour/connection/transaction/BUILD +++ b/nodejs/test/behaviour/connection/transaction/BUILD @@ -51,5 +51,6 @@ typedb_behaviour_node_test( "//nodejs/test/behaviour/connection/database:steps", "//nodejs/test/behaviour/connection/session:steps", "//nodejs/test/behaviour/query:steps", + "//nodejs/test/behaviour/util:steps" ], ) diff --git a/nodejs/test/behaviour/connection/user/BUILD b/nodejs/test/behaviour/connection/user/BUILD index 4c2cad80d4..90b988b801 100644 --- a/nodejs/test/behaviour/connection/user/BUILD +++ b/nodejs/test/behaviour/connection/user/BUILD @@ -47,5 +47,7 @@ node_cucumber_test( name = "test-enterprise", steps = "@//nodejs/test/behaviour/connection:steps-enterprise", features = ["@vaticle_typedb_behaviour//connection:user.feature"], - data = [":steps"], + data = [ + ":steps", + ], ) diff --git a/nodejs/test/behaviour/rules.bzl b/nodejs/test/behaviour/rules.bzl index 879fbd35c7..32e0281fb5 100644 --- a/nodejs/test/behaviour/rules.bzl +++ b/nodejs/test/behaviour/rules.bzl @@ -44,7 +44,7 @@ def node_cucumber_test(name, features, data, steps, **kwargs): no_copy_to_bin = features, fixed_args = [ "--publish-quiet", "--strict", - "--tags 'not @ignore and not @ignore-typedb and not @ignore-typedb-driver-nodejs and not @ignore-driver-nodejs'", + "--tags 'not @ignore and not @ignore-typedb-driver and not @ignore-typedb-driver-nodejs'", "--require", "nodejs/test/**/*.js", ] + ["$(location {})".format(feature) for feature in features], **kwargs, diff --git a/nodejs/test/integration/test-concept.js b/nodejs/test/integration/test-concept.js index 57ff463bde..488a9a8f02 100644 --- a/nodejs/test/integration/test-concept.js +++ b/nodejs/test/integration/test-concept.js @@ -351,8 +351,8 @@ async function run() { const firstPlayer = playersByRoleType.next().value; assert(firstPlayer.label.scopedName === "lion-family:lion-cub"); await firstLionFamily.removeRolePlayer(tx, lionCub, firstLion); - const lionFamilyCleanedUp = await firstLionFamily.isDeleted(tx); - assert(lionFamilyCleanedUp); + const emptyLionFamilyExists = !(await firstLionFamily.isDeleted(tx)); + assert(emptyLionFamilyExists); await tx.rollback(); await tx.close(); console.log(`Relation methods - SUCCESS`); diff --git a/nodejs/user/UserManagerImpl.ts b/nodejs/user/UserManagerImpl.ts index 2f98842c89..49062e4171 100644 --- a/nodejs/user/UserManagerImpl.ts +++ b/nodejs/user/UserManagerImpl.ts @@ -25,6 +25,9 @@ import {RequestBuilder} from "../common/rpc/RequestBuilder"; import {UserImpl} from "../dependencies_internal"; import {ServerDriver, TypeDBDriverImpl} from "../connection/TypeDBDriverImpl"; import {TypeDBDatabaseImpl} from "../connection/TypeDBDatabaseImpl"; +import {TypeDBDriverError} from "../common/errors/TypeDBDriverError"; +import {ErrorMessage} from "../common/errors/ErrorMessage"; +import USER_MANAGEMENT_ENTERPRISE_ONLY = ErrorMessage.Driver.USER_MANAGEMENT_ENTERPRISE_ONLY; export class UserManagerImpl implements UserManager { static _SYSTEM_DB = "_system"; @@ -73,6 +76,7 @@ export class UserManagerImpl implements UserManager { } async runFailsafe(task: (driver: ServerDriver) => Promise): Promise { + if (!this._driver.isEnterprise()) throw new TypeDBDriverError(USER_MANAGEMENT_ENTERPRISE_ONLY); return await (await TypeDBDatabaseImpl.get(UserManagerImpl._SYSTEM_DB, this._driver)).runOnPrimaryReplica(task); } } diff --git a/python/README.md b/python/README.md index 49ca52fe7e..e47dc5258c 100644 --- a/python/README.md +++ b/python/README.md @@ -1,10 +1,10 @@ # TypeDB Python Driver ## Driver Architecture -To learn about the mechanism that a TypeDB Driver uses to set up communication with databases running on the TypeDB Server, refer to the [Clients Overview](https://typedb.com/docs/clients/2.x/clients). +To learn about the mechanism that a TypeDB Driver uses to set up communication with databases running on the TypeDB Server, refer to the [Clients Overview](https://typedb.com/docs/clients/overview). ## API Reference -To learn about the methods available for executing queries and retrieving their answers using Driver Python, refer to the [API Reference](https://typedb.com/docs/clients/2.x/python/python-api-ref). +To learn about the methods available for executing queries and retrieving their answers using Driver Python, refer to the [API Reference](https://typedb.com/docs/clients/python-driver/api-reference). ## Install TypeDB Driver for Python through Pip ``` diff --git a/python/docs/schema/Type.adoc b/python/docs/schema/Type.adoc index 9444dfda56..2462c710b0 100644 --- a/python/docs/schema/Type.adoc +++ b/python/docs/schema/Type.adoc @@ -217,7 +217,7 @@ type_.is_type() [source,python] ---- -set_label(transaction: TypeDBTransaction, new_label: Label) -> Promise[None] +set_label(transaction: TypeDBTransaction, new_label: str) -> Promise[None] ---- Renames the label of the type. The new label must remain unique. @@ -229,7 +229,7 @@ Renames the label of the type. The new label must remain unique. |=== |Name |Description |Type |Default Value a| `transaction` a| The current transaction a| `TypeDBTransaction` a| -a| `new_label` a| The new ``Label`` to be given to the type. a| `Label` a| +a| `new_label` a| The new name to be given to the type. a| `str` a| |=== [caption=""] diff --git a/python/tests/behaviour/background/core/environment.py b/python/tests/behaviour/background/core/environment.py index 6208a70be4..d91b0f58fb 100644 --- a/python/tests/behaviour/background/core/environment.py +++ b/python/tests/behaviour/background/core/environment.py @@ -24,7 +24,7 @@ from tests.behaviour.background import environment_base from tests.behaviour.context import Context -IGNORE_TAGS = ["ignore", "ignore-driver-python", "ignore-typedb-driver-python"] +IGNORE_TAGS = ["ignore", "ignore-typedb-driver", "ignore-typedb-driver-python"] def before_all(context: Context): diff --git a/python/tests/behaviour/background/enterprise/environment.py b/python/tests/behaviour/background/enterprise/environment.py index 964f1ebaf5..bfd321c9db 100644 --- a/python/tests/behaviour/background/enterprise/environment.py +++ b/python/tests/behaviour/background/enterprise/environment.py @@ -25,7 +25,7 @@ from tests.behaviour.background import environment_base from tests.behaviour.context import Context -IGNORE_TAGS = ["ignore", "ignore-driver-python", "ignore-typedb-driver-python", "ignore-typedb-enterprise-driver-python"] +IGNORE_TAGS = ["ignore", "ignore-typedb-driver", "ignore-typedb-driver-python"] def before_all(context: Context): @@ -44,9 +44,9 @@ def before_scenario(context: Context, scenario): def setup_context_driver(context, username, password): - credential = TypeDBCredential(username, password, tls_root_ca_path=context.credential_root_ca_path) + credential = TypeDBCredential(username, password, tls_enabled=True, tls_root_ca_path=context.credential_root_ca_path) context.driver = TypeDB.enterprise_driver(addresses=["localhost:" + context.config.userdata["port"]], - credential=credential) + credential=credential) context.session_options = TypeDBOptions(infer=True) context.transaction_options = TypeDBOptions(infer=True) diff --git a/python/typedb/api/concept/type/type.py b/python/typedb/api/concept/type/type.py index 76446cdc38..12e38b5325 100644 --- a/python/typedb/api/concept/type/type.py +++ b/python/typedb/api/concept/type/type.py @@ -50,12 +50,12 @@ def get_label(self) -> Label: pass @abstractmethod - def set_label(self, transaction: TypeDBTransaction, new_label: Label) -> Promise[None]: + def set_label(self, transaction: TypeDBTransaction, new_label: str) -> Promise[None]: """ Renames the label of the type. The new label must remain unique. :param transaction: The current transaction - :param new_label: The new ``Label`` to be given to the type. + :param new_label: The new name to be given to the type. :return: Examples diff --git a/python/typedb/api/connection/credential.py b/python/typedb/api/connection/credential.py index 9bfeb651e2..1ee073d373 100644 --- a/python/typedb/api/connection/credential.py +++ b/python/typedb/api/connection/credential.py @@ -48,7 +48,7 @@ class TypeDBCredential(NativeWrapper[NativeCredential]): """ def __init__(self, username: str, password: str, *, tls_root_ca_path: Optional[str] = None, - tls_enabled: bool = True): + tls_enabled: bool = False): if tls_root_ca_path is not None and not tls_enabled: raise TypeDBDriverException(ENTERPRISE_CREDENTIAL_INCONSISTENT) super().__init__(credential_new(username, password, tls_root_ca_path, tls_enabled)) diff --git a/python/typedb/common/exception.py b/python/typedb/common/exception.py index 21a79cbfa5..473c2ad27d 100644 --- a/python/typedb/common/exception.py +++ b/python/typedb/common/exception.py @@ -85,7 +85,7 @@ class DriverErrorMessage(ErrorMessage): """ def __init__(self, code: int, message: str): - super(DriverErrorMessage, self).__init__(code_prefix="CLI", code_number=code, message_prefix="Driver Error", + super(DriverErrorMessage, self).__init__(code_prefix="DRI", code_number=code, message_prefix="Driver Error", message_body=message) diff --git a/python/typedb/concept/type/thing_type.py b/python/typedb/concept/type/thing_type.py index ffaa791425..b6510db24d 100644 --- a/python/typedb/concept/type/thing_type.py +++ b/python/typedb/concept/type/thing_type.py @@ -81,7 +81,7 @@ def is_deleted(self, transaction: _Transaction) -> Promise[bool]: promise = thing_type_is_deleted(transaction.native_object, self.native_object) return Promise(lambda: bool_promise_resolve(promise)) - def set_label(self, transaction: _Transaction, new_label: Label) -> Promise[None]: + def set_label(self, transaction: _Transaction, new_label: str) -> Promise[None]: promise = thing_type_set_label(transaction.native_object, self.native_object, new_label) return Promise(lambda: void_promise_resolve(promise)) diff --git a/python/typedb/connection/driver.py b/python/typedb/connection/driver.py index 7b9ae5b583..2c67609dab 100644 --- a/python/typedb/connection/driver.py +++ b/python/typedb/connection/driver.py @@ -91,4 +91,5 @@ def __exit__(self, exc_type, exc_val, exc_tb): return False def close(self) -> None: - connection_force_close(self._native_connection) + if not self.is_open(): + connection_force_close(self._native_connection) diff --git a/rust/README.md b/rust/README.md index e6bb399260..c6653250f0 100644 --- a/rust/README.md +++ b/rust/README.md @@ -7,12 +7,12 @@ [![Stack Overflow](https://img.shields.io/badge/stackoverflow-typeql-3dce8c.svg)](https://stackoverflow.com/questions/tagged/typeql) ## Driver Architecture -To learn about the mechanism that a TypeDB Driver uses to set up communication with databases running on the TypeDB Server, refer to the [Clients Overview](https://typedb.com/docs/clients/2.x/clients). +To learn about the mechanism that a TypeDB Driver uses to set up communication with databases running on the TypeDB Server, refer to the [Clients Overview](https://typedb.com/docs/clients/overview). The TypeDB Driver for Rust provides a fully async API that supports multiple async runtimes or a synchronous interface gated by the `sync` feature. ## API Reference -To learn about the methods available for executing queries and retrieving their answers using Driver Rust, refer to the [API Reference](https://typedb.com/docs/clients/2.x/rust/rust-api-ref). +To learn about the methods available for executing queries and retrieving their answers using Driver Rust, refer to the [API Reference](https://typedb.com/docs/clients/rust-driver/api-reference). ## Quickstart 1. Import `typedb-driver` through Cargo: diff --git a/rust/docs/connection/Connection.adoc b/rust/docs/connection/Connection.adoc index c76bce08bd..6b8ccc9ece 100644 --- a/rust/docs/connection/Connection.adoc +++ b/rust/docs/connection/Connection.adoc @@ -31,6 +31,30 @@ Result connection.force_close() ---- +[#_struct_Connection_method_is_enterprise] +==== is_enterprise + +[source,rust] +---- +pub fn is_enterprise(&self) -> bool +---- + +Check if the connection is to an Enterprise server. + +[caption=""] +.Returns +[source,rust] +---- +bool +---- + +[caption=""] +.Code examples +[source,rust] +---- +connection.is_enterprise() +---- + [#_struct_Connection_method_is_open] ==== is_open diff --git a/rust/docs/errors/ConnectionError.adoc b/rust/docs/errors/ConnectionError.adoc index 7d94de7ccb..9c9e4fad5e 100644 --- a/rust/docs/errors/ConnectionError.adoc +++ b/rust/docs/errors/ConnectionError.adoc @@ -9,24 +9,27 @@ |=== |Variant a| `BrokenPipe()` +a| `ConnectionFailed()` a| `ConnectionIsClosed()` -a| `ConnectionRefused()` a| `DatabaseDoesNotExist(String)` a| `EnterpriseAllNodesFailed(String)` a| `EnterpriseEndpointEncrypted()` a| `EnterpriseReplicaNotPrimary()` a| `EnterpriseSSLCertificateNotValidated()` a| `EnterpriseTokenCredentialInvalid()` -a| `EnterpriseUnableToConnect(String)` a| `InvalidResponseField(&'static str)` a| `MissingResponseField(&'static str)` a| `RPCMethodUnavailable(String)` +a| `ServerConnectionFailed(String)` +a| `ServerConnectionFailedStatusError(String)` +a| `ServerConnectionFailedWithError(String)` a| `SessionCloseFailed()` a| `SessionIsClosed()` a| `TransactionIsClosed()` a| `TransactionIsClosedWithErrors(String)` -a| `UnableToConnect()` +a| `UnexpectedResponse(String)` a| `UnknownRequestId(ID)` +a| `UserManagementEnterpriseOnly()` |=== // end::enum_constants[] diff --git a/rust/src/common/error.rs b/rust/src/common/error.rs index afb901d750..6dbb86c2d8 100644 --- a/rust/src/common/error.rs +++ b/rust/src/common/error.rs @@ -38,22 +38,28 @@ error_messages! { ConnectionError 4: "The transaction is closed and no further operation is allowed.", TransactionIsClosedWithErrors(String) = 5: "The transaction is closed because of the error(s):\n{}", - UnableToConnect() = - 6: "Unable to connect to TypeDB server.", DatabaseDoesNotExist(String) = - 9: "The database '{}' does not exist.", + 6: "The database '{}' does not exist.", MissingResponseField(&'static str) = - 10: "Missing field in message received from server: '{}'.", + 7: "Missing field in message received from server: '{}'.", UnknownRequestId(RequestID) = - 11: "Received a response with unknown request id '{}'", + 8: "Received a response with unknown request id '{}'", InvalidResponseField(&'static str) = - 12: "Invalid field in message received from server: '{}'.", - EnterpriseUnableToConnect(String) = - 13: "Unable to connect to TypeDB Enterprise. Attempted connecting to the enterprise members, but none are available: '{}'.", + 9: "Invalid field in message received from server: '{}'.", + UnexpectedResponse(String) = + 10: "Received unexpected response from server: '{}'.", + ServerConnectionFailed(String) = + 11: "Unable to connect to TypeDB server(s) at: \n{}", + ServerConnectionFailedWithError(String) = + 12: "Unable to connect to TypeDB server(s), received errors: \n{}", + ServerConnectionFailedStatusError(String) = + 13: "Unable to connect to TypeDB server(s), received network error: \n{}", + UserManagementEnterpriseOnly() = + 14: "User management is only available in TypeDB Enterprise servers.", EnterpriseReplicaNotPrimary() = - 14: "The replica is not the primary replica.", + 15: "The replica is not the primary replica.", EnterpriseAllNodesFailed(String) = - 15: "Attempted connecting to all enterprise members, but the following errors occurred: \n{}.", + 16: "Attempted connecting to all TypeDB Enterprise servers, but the following errors occurred: \n{}.", EnterpriseTokenCredentialInvalid() = 17: "Invalid token credential.", SessionCloseFailed() = @@ -64,8 +70,8 @@ error_messages! { ConnectionError 20: "SSL handshake with TypeDB Enterprise failed: the server's identity could not be verified. Possible CA mismatch.", BrokenPipe() = 21: "Stream closed because of a broken pipe. This could happen if you are attempting to connect to an unencrypted enterprise instance using a TLS-enabled credential.", - ConnectionRefused() = - 22: "Connection refused. This could happen because of a misconfigured server SSL certificate, or network failures.", + ConnectionFailed() = + 22: "Connection failed. Please check the server is running and the address is accessible. Encrypted Enterprise endpoints may also have misconfigured SSL certificates.", } error_messages! { InternalError @@ -73,7 +79,7 @@ error_messages! { InternalError RecvError() = 1: "Channel is closed.", SendError() = - 2: "Channel is closed.", + 2: "Unable to send response over callback channel (receiver dropped).", UnexpectedRequestType(String) = 3: "Unexpected request type for remote procedure call: {}.", UnexpectedResponseType(String) = @@ -130,9 +136,9 @@ impl Error { } else if status_message.contains("UnknownIssuer") { Error::Connection(ConnectionError::EnterpriseSSLCertificateNotValidated()) } else if status_message.contains("Connection refused") { - Error::Connection(ConnectionError::ConnectionRefused()) + Error::Connection(ConnectionError::ConnectionFailed()) } else { - Error::Connection(ConnectionError::UnableToConnect()) + Error::Connection(ConnectionError::ServerConnectionFailedStatusError(status_message.to_owned())) } } } @@ -182,7 +188,7 @@ impl From for Error { if status.code() == Code::Unavailable { Self::parse_unavailable(status.message()) } else if status.code() == Code::Unknown || is_rst_stream(&status) { - Self::Connection(ConnectionError::UnableToConnect()) + Self::Connection(ConnectionError::ServerConnectionFailedStatusError(status.message().to_owned())) } else if status.code() == Code::Unimplemented { Self::Connection(ConnectionError::RPCMethodUnavailable(status.message().to_owned())) } else { @@ -209,8 +215,8 @@ impl From for Error { } impl From> for Error { - fn from(err: tokio::sync::mpsc::error::SendError) -> Self { - Self::Other(err.to_string()) + fn from(_err: tokio::sync::mpsc::error::SendError) -> Self { + Self::Internal(InternalError::SendError()) } } diff --git a/rust/src/connection/connection.rs b/rust/src/connection/connection.rs index b88f547ac9..4015188732 100644 --- a/rust/src/connection/connection.rs +++ b/rust/src/connection/connection.rs @@ -60,6 +60,7 @@ pub struct Connection { server_connections: HashMap, background_runtime: Arc, username: Option, + is_enterprise: bool, } impl Connection { @@ -82,13 +83,14 @@ impl Connection { .servers_all()? .into_iter() .exactly_one() - .map_err(|_| ConnectionError::UnableToConnect())?; + .map_err(|e| ConnectionError::ServerConnectionFailedStatusError(e.to_string()))?; server_connection.set_address(address.clone()); match server_connection.validate() { Ok(()) => Ok(Self { server_connections: [(address, server_connection)].into(), background_runtime, username: None, + is_enterprise: false, }), Err(err) => Err(err), } @@ -137,7 +139,12 @@ impl Connection { errors.into_iter().map(|err| err.to_string()).collect::>().join("\n"), ))? } else { - Ok(Self { server_connections, background_runtime, username: Some(credential.username().to_string()) }) + Ok(Self { + server_connections, + background_runtime, + username: Some(credential.username().to_string()), + is_enterprise: true, + }) } } @@ -146,26 +153,31 @@ impl Connection { addresses: Vec
, credential: Credential, ) -> Result> { - for address in addresses { + for address in &addresses { let server_connection = ServerConnection::new_enterprise(background_runtime.clone(), address.clone(), credential.clone()); match server_connection { Ok(server_connection) => match server_connection.servers_all() { Ok(servers) => return Ok(servers.into_iter().collect()), Err(Error::Connection( - ConnectionError::UnableToConnect() | ConnectionError::ConnectionRefused(), + ConnectionError::ServerConnectionFailedStatusError(_) | ConnectionError::ConnectionFailed(), )) => (), Err(err) => Err(err)?, }, - Err(Error::Connection(ConnectionError::UnableToConnect() | ConnectionError::ConnectionRefused())) => (), + Err(Error::Connection( + ConnectionError::ServerConnectionFailedStatusError(_) | ConnectionError::ConnectionFailed(), + )) => (), Err(err) => Err(err)?, } } - Err(ConnectionError::UnableToConnect().into()) + Err(ConnectionError::ServerConnectionFailed( + addresses.into_iter().map(|a| a.to_string()).collect::>().join(","), + ) + .into()) } /// Checks it this connection is opened. - /// + // /// # Examples /// /// ```rust @@ -175,6 +187,17 @@ impl Connection { self.background_runtime.is_open() } + /// Check if the connection is to an Enterprise server. + /// + /// # Examples + /// + /// ```rust + /// connection.is_enterprise() + /// ``` + pub fn is_enterprise(&self) -> bool { + self.is_enterprise + } + /// Closes this connection. /// /// # Examples @@ -211,8 +234,8 @@ impl Connection { } pub(crate) fn unable_to_connect_error(&self) -> Error { - Error::Connection(ConnectionError::EnterpriseUnableToConnect( - self.addresses().map(Address::to_string).collect::>().join(","), + Error::Connection(ConnectionError::ServerConnectionFailedStatusError( + self.addresses().map(Address::to_string).collect::>().join(", "), )) } } @@ -250,7 +273,7 @@ impl ServerConnection { pub(crate) fn validate(&self) -> Result { match self.request_blocking(Request::ConnectionOpen)? { Response::ConnectionOpen => Ok(()), - _other => Err(ConnectionError::UnableToConnect().into()), + _other => Err(ConnectionError::UnexpectedResponse(format!("{_other:?}")).into()), } } diff --git a/rust/src/connection/network/transmitter/response_sink.rs b/rust/src/connection/network/transmitter/response_sink.rs index 0d2ad5369d..5718a087cc 100644 --- a/rust/src/connection/network/transmitter/response_sink.rs +++ b/rust/src/connection/network/transmitter/response_sink.rs @@ -20,7 +20,7 @@ */ use crossbeam::channel::Sender as SyncSender; -use log::error; +use log::{debug, error}; use tokio::sync::{mpsc::UnboundedSender, oneshot::Sender as AsyncOneshotSender}; use crate::{ @@ -43,8 +43,10 @@ impl ResponseSink { Self::BlockingOneShot(sink) => sink.send(response).map_err(Error::from), Self::Streamed(sink) => sink.send(response).map_err(Error::from), }; - if let Err(err) = result { - error!("{}", err); + match result { + Err(Error::Internal(err @ InternalError::SendError())) => debug!("{err}"), + Err(err) => error!("{err}"), + Ok(()) => (), } } @@ -53,8 +55,10 @@ impl ResponseSink { Self::Streamed(sink) => sink.send(response).map_err(Error::from), _ => unreachable!("attempted to stream over a one-shot callback"), }; - if let Err(err) = result { - error!("{}", err); + match result { + Err(Error::Internal(err @ InternalError::SendError())) => debug!("{err}"), + Err(err) => error!("{err}"), + Ok(()) => (), } } diff --git a/rust/src/database/database.rs b/rust/src/database/database.rs index ace0785306..6050e073ce 100644 --- a/rust/src/database/database.rs +++ b/rust/src/database/database.rs @@ -188,7 +188,9 @@ impl Database { match task(replica.database.clone(), self.connection.connection(&replica.address)?.clone(), is_first_run) .await { - Err(Error::Connection(ConnectionError::UnableToConnect() | ConnectionError::ConnectionRefused())) => { + Err(Error::Connection( + ConnectionError::ServerConnectionFailedStatusError(_) | ConnectionError::ConnectionFailed(), + )) => { debug!("Unable to connect to {}. Attempting next server.", replica.address); } res => return res, @@ -217,8 +219,8 @@ impl Database { { Err(Error::Connection( ConnectionError::EnterpriseReplicaNotPrimary() - | ConnectionError::UnableToConnect() - | ConnectionError::ConnectionRefused(), + | ConnectionError::ServerConnectionFailedStatusError(_) + | ConnectionError::ConnectionFailed(), )) => { debug!("Primary replica error, waiting..."); Self::wait_for_primary_replica_selection().await; @@ -323,8 +325,8 @@ impl Replica { } Err(Error::Connection( ConnectionError::DatabaseDoesNotExist(_) - | ConnectionError::UnableToConnect() - | ConnectionError::ConnectionRefused(), + | ConnectionError::ServerConnectionFailedStatusError(_) + | ConnectionError::ConnectionFailed(), )) => { error!( "Failed to fetch replica info for database '{}' from {}. Attempting next server.", diff --git a/rust/src/database/database_manager.rs b/rust/src/database/database_manager.rs index 4411d1f6de..a2dbeb1449 100644 --- a/rust/src/database/database_manager.rs +++ b/rust/src/database/database_manager.rs @@ -118,7 +118,7 @@ impl DatabaseManager { Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), } } - Err(ConnectionError::EnterpriseAllNodesFailed(error_buffer.join("\n")))? + Err(ConnectionError::ServerConnectionFailedWithError(error_buffer.join("\n")))? } #[cfg_attr(feature = "sync", maybe_async::must_be_sync)] @@ -144,6 +144,6 @@ impl DatabaseManager { Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), } } - Err(ConnectionError::EnterpriseAllNodesFailed(error_buffer.join("\n")))? + Err(ConnectionError::ServerConnectionFailedWithError(error_buffer.join("\n")))? } } diff --git a/rust/src/user/user_manager.rs b/rust/src/user/user_manager.rs index eaa16575cc..2f14b98df7 100644 --- a/rust/src/user/user_manager.rs +++ b/rust/src/user/user_manager.rs @@ -22,7 +22,9 @@ #[cfg(not(feature = "sync"))] use std::future::Future; -use crate::{common::Result, connection::ServerConnection, Connection, DatabaseManager, User}; +use crate::{ + common::Result, connection::ServerConnection, error::ConnectionError, Connection, DatabaseManager, Error, User, +}; /// Provides access to all user management methods. #[derive(Clone, Debug)] @@ -184,10 +186,14 @@ impl UserManager { F: Fn(ServerConnection) -> P, P: Future>, { - DatabaseManager::new(self.connection.clone()) - .get(Self::SYSTEM_DB) - .await? - .run_failsafe(|_, server_connection, _| task(server_connection)) - .await + if !self.connection.is_enterprise() { + Err(Error::Connection(ConnectionError::UserManagementEnterpriseOnly())) + } else { + DatabaseManager::new(self.connection.clone()) + .get(Self::SYSTEM_DB) + .await? + .run_failsafe(|_, server_connection, _| task(server_connection)) + .await + } } } diff --git a/rust/tests/behaviour/connection/session/steps.rs b/rust/tests/behaviour/connection/session/steps.rs index 8e11cf93ee..15a794d854 100644 --- a/rust/tests/behaviour/connection/session/steps.rs +++ b/rust/tests/behaviour/connection/session/steps.rs @@ -19,6 +19,8 @@ * under the License. */ +use std::time::Duration; + use cucumber::{gherkin::Step, given, then, when}; use futures::{future::try_join_all, TryFutureExt}; use typedb_driver::{Session, SessionType}; @@ -33,21 +35,21 @@ generic_step_impl! { pub async fn connection_open_schema_session_for_database(context: &mut Context, name: String) { context .session_trackers - .push(Session::new(context.databases.get(name).await.unwrap(), SessionType::Schema).await.unwrap().into()); + .push(Session::new_with_options(context.databases.get(name).await.unwrap(), SessionType::Schema, context.session_options.clone()).await.unwrap().into()); } #[step(expr = "connection open (data )session for database: {word}")] pub async fn connection_open_data_session_for_database(context: &mut Context, name: String) { context .session_trackers - .push(Session::new(context.databases.get(name).await.unwrap(), SessionType::Data).await.unwrap().into()); + .push(Session::new_with_options(context.databases.get(name).await.unwrap(), SessionType::Data, context.session_options.clone()).await.unwrap().into()); } #[step(expr = "connection open schema session(s) for database(s):")] async fn connection_open_schema_sessions_for_databases(context: &mut Context, step: &Step) { for name in util::iter_table(step) { context.session_trackers.push( - Session::new(context.databases.get(name).await.unwrap(), SessionType::Schema).await.unwrap().into(), + Session::new_with_options(context.databases.get(name).await.unwrap(), SessionType::Schema, context.session_options.clone()).await.unwrap().into(), ); } } @@ -56,7 +58,7 @@ generic_step_impl! { async fn connection_open_data_sessions_for_databases(context: &mut Context, step: &Step) { for name in util::iter_table(step) { context.session_trackers.push( - Session::new(context.databases.get(name).await.unwrap(), SessionType::Data).await.unwrap().into(), + Session::new_with_options(context.databases.get(name).await.unwrap(), SessionType::Data, context.session_options.clone()).await.unwrap().into(), ); } } @@ -65,7 +67,7 @@ generic_step_impl! { async fn connection_open_data_sessions_in_parallel_for_databases(context: &mut Context, step: &Step) { let new_sessions = try_join_all( util::iter_table(step) - .map(|name| context.databases.get(name).and_then(|db| Session::new(db, SessionType::Data))), + .map(|name| context.databases.get(name).and_then(|db| Session::new_with_options(db, SessionType::Data, context.session_options.clone()))), ) .await .unwrap(); @@ -106,11 +108,11 @@ generic_step_impl! { } #[step(expr = "set session option {word} to: {word}")] - async fn set_session_option_to(_context: &mut Context, _option: String, _value: String) { - todo!() - // if (!optionSetters.containsKey(option)) { - // throw new RuntimeException("Unrecognised option: " + option); - // } - // optionSetters.get(option).accept(sessionOptions, value); + async fn set_session_option_to(context: &mut Context, option: String, value: String) { + if option == "session-idle-timeout-millis" { + context.session_options.session_idle_timeout = Some(Duration::from_millis(value.parse().unwrap())); + } else { + todo!("Session option not recognised: {}", option); + } } } diff --git a/rust/tests/behaviour/connection/steps.rs b/rust/tests/behaviour/connection/steps.rs index d5aa3ed7c0..a7a4132be8 100644 --- a/rust/tests/behaviour/connection/steps.rs +++ b/rust/tests/behaviour/connection/steps.rs @@ -40,7 +40,7 @@ generic_step_impl! { &login, &password, Some(&context.tls_root_ca), - ).unwrap(), + ).unwrap() ); context.set_connection(connection.unwrap()); } diff --git a/rust/tests/behaviour/connection/transaction/steps.rs b/rust/tests/behaviour/connection/transaction/steps.rs index 418c953970..7132e69bc3 100644 --- a/rust/tests/behaviour/connection/transaction/steps.rs +++ b/rust/tests/behaviour/connection/transaction/steps.rs @@ -19,6 +19,8 @@ * under the License. */ +use std::time::Duration; + use cucumber::{gherkin::Step, given, then, when}; use typedb_driver::TransactionType; @@ -35,7 +37,7 @@ generic_step_impl! { #[step(expr = "(for each )session(,) open(s) transaction(s) of type: {transaction_type}")] pub async fn session_opens_transaction_of_type(context: &mut Context, type_: TransactionTypeParam) { for session_tracker in &mut context.session_trackers { - session_tracker.open_transaction(type_.transaction_type).await.unwrap(); + session_tracker.open_transaction(type_.transaction_type, context.transaction_options.clone()).await.unwrap(); } } @@ -44,7 +46,7 @@ generic_step_impl! { for type_ in iter_table(step) { let transaction_type = type_.parse::().unwrap().transaction_type; for session_tracker in &mut context.session_trackers { - session_tracker.open_transaction(transaction_type).await.unwrap(); + session_tracker.open_transaction(transaction_type, context.transaction_options.clone()).await.unwrap(); } } } @@ -55,7 +57,7 @@ generic_step_impl! { type_: TransactionTypeParam, ) { for session_tracker in &mut context.session_trackers { - assert!(session_tracker.open_transaction(type_.transaction_type).await.is_err()); + assert!(session_tracker.open_transaction(type_.transaction_type, context.transaction_options.clone()).await.is_err()); } } @@ -64,7 +66,7 @@ generic_step_impl! { for type_ in iter_table(step) { let transaction_type = type_.parse::().unwrap().transaction_type; for session_tracker in &mut context.session_trackers { - assert!(session_tracker.open_transaction(transaction_type).await.is_err()); + assert!(session_tracker.open_transaction(transaction_type, context.transaction_options.clone()).await.is_err()); } } } @@ -161,8 +163,17 @@ generic_step_impl! { let transaction_type = type_.parse::().unwrap().transaction_type; for session_tracker in &mut context.session_trackers { // FIXME parallel - session_tracker.open_transaction(transaction_type).await.unwrap(); + session_tracker.open_transaction(transaction_type, context.transaction_options.clone()).await.unwrap(); } } } + + #[step(expr = "set transaction option {word} to: {word}")] + async fn set_transaction_option_to(context: &mut Context, option: String, value: String) { + if option == "transaction-timeout-millis" { + context.transaction_options.transaction_timeout = Some(Duration::from_millis(value.parse().unwrap())) + } else { + todo!("Transaction Option not recognised: {}", option); + } + } } diff --git a/rust/tests/behaviour/mod.rs b/rust/tests/behaviour/mod.rs index 0cb1337d02..0b4e83eb28 100644 --- a/rust/tests/behaviour/mod.rs +++ b/rust/tests/behaviour/mod.rs @@ -39,7 +39,7 @@ use typedb_driver::{ answer::{ConceptMap, ConceptMapGroup, ValueGroup, JSON}, concept::{Attribute, AttributeType, Entity, EntityType, Relation, RelationType, Thing, Value}, logic::Rule, - Connection, Credential, Database, DatabaseManager, Result as TypeDBResult, Transaction, UserManager, + Connection, Credential, Database, DatabaseManager, Options, Result as TypeDBResult, Transaction, UserManager, }; use self::session_tracker::SessionTracker; @@ -47,6 +47,8 @@ use self::session_tracker::SessionTracker; #[derive(Debug, World)] pub struct Context { pub tls_root_ca: PathBuf, + pub session_options: Options, + pub transaction_options: Options, pub connection: Connection, pub databases: DatabaseManager, pub users: UserManager, @@ -91,11 +93,13 @@ impl Context { } fn is_ignore_tag(t: &String) -> bool { - t == "ignore" || t == "ignore-typedb" || t == "ignore-driver-rust" || t == "ignore-typedb-driver-rust" + t == "ignore" || t == "ignore-typedb-driver" || t == "ignore-typedb-driver-rust" } pub async fn after_scenario(&mut self) -> TypeDBResult { sleep(Context::STEP_REATTEMPT_SLEEP).await; + self.session_options = Options::new(); + self.transaction_options = Options::new().infer(true); self.set_connection(Connection::new_enterprise( &["localhost:11729", "localhost:21729", "localhost:31729"], Credential::with_tls(Context::ADMIN_USERNAME, Context::ADMIN_PASSWORD, Some(&self.tls_root_ca))?, @@ -220,6 +224,8 @@ impl Default for Context { let tls_root_ca = PathBuf::from( std::env::var("ROOT_CA").expect("ROOT_CA environment variable needs to be set for enterprise tests to run"), ); + let session_options = Options::new(); + let transaction_options = Options::new().infer(true); let connection = Connection::new_enterprise( &["localhost:11729", "localhost:21729", "localhost:31729"], Credential::with_tls(Context::ADMIN_USERNAME, Context::ADMIN_PASSWORD, Some(&tls_root_ca)).unwrap(), @@ -229,6 +235,8 @@ impl Default for Context { let users = UserManager::new(connection.clone()); Self { tls_root_ca, + session_options, + transaction_options, connection, databases, users, diff --git a/rust/tests/behaviour/session_tracker.rs b/rust/tests/behaviour/session_tracker.rs index 2a4aa6f1d7..025aeafb12 100644 --- a/rust/tests/behaviour/session_tracker.rs +++ b/rust/tests/behaviour/session_tracker.rs @@ -51,15 +51,15 @@ impl SessionTracker { &self.session } - pub async fn open_transaction(&mut self, transaction_type: TransactionType) -> typedb_driver::Result { - let options = match transaction_type { - TransactionType::Write => Options::new(), - TransactionType::Read => Options::new().infer(true), - }; + pub async fn open_transaction( + &mut self, + transaction_type: TransactionType, + transaction_options: Options, + ) -> typedb_driver::Result { unsafe { // SAFETY: the transactions tracked by the SessionTracker instance borrow SessionTracker::session. // As long as SessionTracker is alive, the transactions are valid. - let transaction = self.session.transaction_with_options(transaction_type, options).await?; + let transaction = self.session.transaction_with_options(transaction_type, transaction_options).await?; self.transactions.push(std::mem::transmute(transaction)); } Ok(()) diff --git a/rust/tests/behaviour/util/steps.rs b/rust/tests/behaviour/util/steps.rs index a421ebb44d..0c7f83cbcb 100644 --- a/rust/tests/behaviour/util/steps.rs +++ b/rust/tests/behaviour/util/steps.rs @@ -22,6 +22,7 @@ use std::env; use cucumber::{given, then, when}; +use tokio::time::{sleep, Duration}; use crate::{behaviour::Context, generic_step_impl}; @@ -30,4 +31,9 @@ generic_step_impl! { async fn set_time_zone(_context: &mut Context, timezone: String) { env::set_var("TZ", timezone); } + + #[step(expr = "wait {word} seconds")] + async fn wait_seconds(_context: &mut Context, seconds: String) { + sleep(Duration::from_secs(seconds.parse().unwrap())).await + } }