From ef0f10567f44dae8f52a99113eb2533c007c92a1 Mon Sep 17 00:00:00 2001 From: toptobes <96998732+toptobes@users.noreply.github.com> Date: Wed, 10 Jul 2024 23:01:22 -0500 Subject: [PATCH] Further fixes and refactors for 1.4.0 (#63) * headers integration testing * better encapsulation on the core classes * updated non-astra example to use updateDbNamespace * reinstated example deps to npm versions * overhauled how tests run * added some missing typescript lang specifiers in tsdoc examples * throw error on missing namespace * more work on test overhaul * tiny bit of tweaking to the test script * commented test script a bit * version script + increase a tests timeout * a * change default test timeout to 1m * use export type where possible * tiny tweak to build script * update to build pipeline to delete "empty" files * fixed bug with datapidbadmin * fixed a couple documentation errors * reverted example deps to npm versions * added exit to if invalid flag passed to test script * better help/error msg for test script * updated vectorize tests to work with non-astra * updated vectorize tests to work with bedrock * linting fix * added missing export --- .np-config.json | 2 +- DEVGUIDE.md | 30 +++-- etc/astra-db-ts.api.md | 76 ++++++++++-- package.json | 10 +- scripts/build.sh | 12 +- scripts/del-empty-dist-files.js | 43 +++++++ scripts/test.sh | 109 ++++++++++++++++++ scripts/version.sh | 3 + src/api/clients/data-api-http-client.ts | 9 +- src/api/index.ts | 6 +- src/client/index.ts | 2 +- src/common/index.ts | 2 +- .../token-providers/static-token-provider.ts | 2 +- src/common/token-providers/token-provider.ts | 2 +- .../userpass-token-providers.ts | 2 +- src/data-api/db.ts | 3 +- .../aws-embedding-headers-provider.ts | 2 +- .../embedding-api-key-header-provider.ts | 2 +- src/data-api/errors.ts | 2 + src/data-api/index.ts | 2 + src/data-api/types/document.ts | 4 +- src/data-api/types/dot-notation.ts | 2 +- src/data-api/types/filter.ts | 4 +- src/data-api/types/index.ts | 56 ++++----- src/data-api/types/utils.ts | 4 +- src/data-api/utils.ts | 2 +- src/devops/data-api-db-admin.ts | 6 +- src/devops/index.ts | 2 +- src/devops/types/admin/admin-common.ts | 2 +- .../db-admin/find-embedding-providers.ts | 2 +- src/devops/types/index.ts | 15 +-- tests/fixtures.ts | 22 +--- .../api/data-api-http-client.test.ts | 4 +- .../client/data-api-client.test.ts | 4 +- .../data-api/collection/bulk-write.test.ts | 6 +- .../collection/count-documents.test.ts | 2 +- .../data-api/collection/delete-all.test.ts | 2 +- .../data-api/collection/delete-many.test.ts | 4 +- .../data-api/collection/delete-one.test.ts | 2 +- .../data-api/collection/distinct.test.ts | 2 +- .../data-api/collection/drop.test.ts | 2 +- .../estimated-document-count.test.ts | 2 +- .../collection/find-one-and-delete.test.ts | 2 +- .../collection/find-one-and-replace.test.ts | 2 +- .../collection/find-one-and-update.test.ts | 2 +- .../data-api/collection/finds.test.ts | 2 +- .../data-api/collection/insert-many.test.ts | 6 +- .../data-api/collection/insert-one.test.ts | 2 +- .../data-api/collection/misc.test.ts | 6 +- .../data-api/collection/options.test.ts | 2 +- .../data-api/collection/replace-one.test.ts | 2 +- .../data-api/collection/update-many.test.ts | 4 +- .../data-api/collection/update-one.test.ts | 2 +- tests/integration/data-api/cursor.test.ts | 2 +- tests/integration/data-api/db.test.ts | 18 ++- tests/integration/data-api/ids.test.ts | 2 +- tests/integration/data-api/vectorize.test.ts | 50 +++++--- tests/integration/devops/db-admin.test.ts | 2 +- tests/integration/devops/lifecycle.test.ts | 2 +- tests/integration/misc/code-samples.test.ts | 2 +- tests/integration/misc/headers.test.ts | 2 +- .../misc/heirarchy-traversal.test.ts | 2 +- tests/integration/misc/quickstart.test.ts | 2 +- tests/integration/misc/timeouts.test.ts | 2 +- tests/prelude.test.ts | 25 ++++ tests/setup.ts | 2 +- 66 files changed, 455 insertions(+), 162 deletions(-) create mode 100644 scripts/del-empty-dist-files.js create mode 100644 scripts/test.sh create mode 100644 scripts/version.sh create mode 100644 tests/prelude.test.ts diff --git a/.np-config.json b/.np-config.json index ab1db0e7..42fa2bca 100644 --- a/.np-config.json +++ b/.np-config.json @@ -1,3 +1,3 @@ { - "testScript": "test:prerelease" + "testScript": "test -- --prerelease" } diff --git a/DEVGUIDE.md b/DEVGUIDE.md index ee1143da..0c89cfe6 100644 --- a/DEVGUIDE.md +++ b/DEVGUIDE.md @@ -10,6 +10,12 @@ Prerequisites: - A clean AstraDB instance with two keyspaces—`default_keyspace` and `other_keyspace` - Copy the `.env.example` file and create a new `.env` file following the example template +```shell +npm run test -- [--all | --light | --coverage | --prerelease] [-f ] [-b] [--args ] +# or +npm run test -- <--types> +``` + ```shell # Run both unit and integration tests npm run test @@ -20,10 +26,21 @@ npm run test -- -f 'unit.' # Run only integration tests npm run test -- -f 'integration.' +# Run all possible tests +npm run test -- --all + +# Run all possible integration tests +npm run test -- --all -f 'integration.' + +# Run all tests that aren't admin/long/vectorize +npm run test -- --light -f 'integration.' + # Run tsc with the noEmit flag to check for type errors -npm run test:types +npm run test -- --types ``` +(bun does not need the extra initial `--` like npm does) + ### Running the tests on local Stargate You can do `sh scripts/start-stargate-4-tests.sh` to spin up an ephemeral Data API on DSE instance which automatically creates the required keyspaces and destroys itself on exit. @@ -56,11 +73,6 @@ env ASTRA_RUN_LONG_TESTS=1 npm run test The `PROD` and `DEV` tags are enabled/disabled automatically, inferred from the astra endpoint URL. -Use the following to run tests with ADMIN and LONG tags automatically enabled (note that doesn't include vectorize tests): -```shell -npm run test:all -``` - ### Adding your own tagged tests To enforce the tags, use the `assertTestsEnabled` function from `test/fixtures.ts`, which will skip the function if the given tag is not enabled. @@ -122,10 +134,10 @@ See `vectorize_credentials.example.json` for—guess what—an example. To run coverage testing, run the following command: ```shell -npm run test:coverage +npm run test -- --coverage ``` -This uses `test:all` under the hood, as well as a "bail early" flag as there's not really a point continuing to run +This uses `test --all` under the hood, as well as a "bail early" flag as there's not really a point continuing to run tests if one of them fails, as the coverage report will be impacted. ## Linting @@ -143,5 +155,5 @@ To build it, just run `npm run build`, which does the following: - Deletes any extraneous `.d.ts` files ## Publishing -I heavily recommend using [np](https://github.com/sindresorhus/np) to publish the package. Running it will involve running `test:prerelease`, and the +I heavily recommend using [np](https://github.com/sindresorhus/np) to publish the package. Running it will involve running `test --prerelease`, and the versioning step will update the api report + update the version in `src/version.ts`. diff --git a/etc/astra-db-ts.api.md b/etc/astra-db-ts.api.md index 3caa0078..2b8a5644 100644 --- a/etc/astra-db-ts.api.md +++ b/etc/astra-db-ts.api.md @@ -120,6 +120,7 @@ export class AstraDbAdmin extends DbAdmin { db(): Db; drop(options?: AdminBlockingOptions): Promise; dropNamespace(namespace: string, options?: AdminBlockingOptions): Promise; + findEmbeddingProviders(options?: WithTimeout): Promise; get id(): string; info(options?: WithTimeout): Promise; listNamespaces(options?: WithTimeout): Promise; @@ -227,6 +228,14 @@ export class CollectionAlreadyExistsError extends DataAPIError { readonly namespace: string; } +// @public +export class CollectionNotFoundError extends DataAPIError { + // @internal + constructor(namespace: string, collectionName: string); + readonly collectionName: string; + readonly namespace: string; +} + // @public export interface CollectionOptions { defaultId?: DefaultIdOptions; @@ -380,6 +389,7 @@ export class DataAPIDbAdmin extends DbAdmin { createNamespace(namespace: string, options?: LocalCreateNamespaceOptions): Promise; db(): Db; dropNamespace(namespace: string, options?: AdminBlockingOptions): Promise; + findEmbeddingProviders(options?: WithTimeout): Promise; listNamespaces(options?: WithTimeout): Promise; } @@ -407,6 +417,15 @@ export interface DataAPIErrorDescriptor { readonly message?: string; } +// @public +export class DataAPIHttpError extends DataAPIError { + // @internal + constructor(resp: FetcherResponseInfo); + readonly body?: string; + readonly raw: FetcherResponseInfo; + readonly status: number; +} + // @public export type DataAPIHttpOptions = DefaultHttpClientOptions | FetchHttpClientOptions | CustomHttpClientOptions; @@ -541,6 +560,7 @@ export abstract class DbAdmin { abstract createNamespace(namespace: string, options?: CreateNamespaceOptions): Promise; abstract db(): Db; abstract dropNamespace(namespace: string, options?: AdminBlockingOptions): Promise; + abstract findEmbeddingProviders(options?: WithTimeout): Promise; abstract listNamespaces(): Promise; } @@ -560,12 +580,6 @@ export interface DbSpawnOptions { token?: string | TokenProvider | null; } -// @public -export class EmbeddingAPIKeyHeaderProvider extends EmbeddingHeadersProvider { - constructor(apiKey: string | nullish); - getHeaders(): Record; -} - // @public export interface DefaultHttpClientOptions { client?: 'default'; @@ -654,6 +668,12 @@ export class DevOpsUnexpectedStateError extends DevOpsAPIError { export interface DropCollectionOptions extends WithTimeout, WithNamespace { } +// @public +export class EmbeddingAPIKeyHeaderProvider extends EmbeddingHeadersProvider { + constructor(apiKey: string | nullish); + getHeaders(): Record; +} + // @public export abstract class EmbeddingHeadersProvider { abstract getHeaders(): Promise> | Record; @@ -661,6 +681,44 @@ export abstract class EmbeddingHeadersProvider { static parseHeaders(token: unknown): EmbeddingHeadersProvider; } +// @public +export interface EmbeddingProviderAuthInfo { + enabled: boolean; + tokens: EmbeddingProviderTokenInfo[]; +} + +// @public +export interface EmbeddingProviderInfo { + displayName: string; + models: EmbeddingProviderModelInfo[]; + parameters: EmbeddingProviderParameterInfo[]; + supportedAuthentication: Record; + url: string; +} + +// @public +export interface EmbeddingProviderModelInfo { + name: string; + parameters: EmbeddingProviderParameterInfo[]; + vectorDimension: number | null; +} + +// @public +export interface EmbeddingProviderParameterInfo { + defaultValue: string; + help: string; + name: string; + required: boolean; + type: string; + validation: Record[]; +} + +// @public +export interface EmbeddingProviderTokenInfo { + accepted: string; + forwarded: string; +} + // @public export class FailedToLoadDefaultClientError extends Error { // @internal @@ -757,6 +815,11 @@ export class FindCursor { toArray(): Promise; } +// @public +export interface FindEmbeddingProvidersResult { + embeddingProviders: Record; +} + // @public export interface FindOneAndDeleteOptions extends WithTimeout { includeResultMetadata?: boolean; @@ -1321,7 +1384,6 @@ export interface VectorizeServiceOptions { export interface VectorOptions { dimension?: number; metric?: 'cosine' | 'euclidean' | 'dot_product'; - // @alpha service?: VectorizeServiceOptions; } diff --git a/package.json b/package.json index 1f25467e..dc4c25f0 100644 --- a/package.json +++ b/package.json @@ -49,14 +49,10 @@ }, "scripts": { "lint": "eslint src/* tests/*", - "test": "ts-mocha --paths -p tsconfig.json tests/unit/**/*.test.ts tests/integration/**/*.test.ts tests/integration/**/**/*.test.ts", - "test:all": "env ASTRA_RUN_LONG_TESTS=1 ASTRA_RUN_ADMIN_TESTS=1 ts-mocha --paths -p tsconfig.json tests/unit/**/*.test.ts tests/integration/**/*.test.ts tests/integration/**/**/*.test.ts", - "test:coverage": "nyc npm run test:all -- -b", - "test:types": "tsc --noEmit --skipLibCheck", - "test:prerelease": "npm run lint && npm run test:types && npm run test:all -- -b --exit", - "build": "sh ./scripts/build.sh", + "test": "sh scripts/test.sh", + "build": "sh scripts/build.sh", "list-embedding-providers": "chmod +x scripts/list-embedding-providers.sh && scripts/list-embedding-providers.sh", - "version": "npm run build && git add src/version.ts etc/astra-db-ts.api.md" + "version": "sh scripts/version.sh" }, "bugs": { "url": "https://github.com/datastax/astra-ts-client/issues" diff --git a/scripts/build.sh b/scripts/build.sh index bd88461a..6481af51 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -7,13 +7,13 @@ rm -rf ./dist node scripts/build-version-file.js > src/version.ts # Transpiles the project -yes | npx tsc --project tsconfig.build.json +npx tsc --project tsconfig.build.json # Replaces alias paths with relative paths (e.g. `@/src/version` -> `../../src/version`) -yes | npx tsc-alias -p tsconfig.build.json +npx tsc-alias -p tsconfig.build.json # Creates the rollup .d.ts, generates an API report in etc/, and cleans up any temp files -yes | npx api-extractor run -c ./api-extractor.jsonc --local && rm -r ./temp +npx api-extractor run -c ./api-extractor.jsonc --local && rm -r ./temp # Uses a more succinct licence notice + removes block comments (the rollup .d.ts file already contains the ts-doc) find ./dist -type f -name '*.js' -exec node scripts/reduce-comments.js {} \; @@ -21,7 +21,13 @@ find ./dist -type f -name '*.js' -exec node scripts/reduce-comments.js {} \; # Adds the missing license notice to the rollup .d.ts node scripts/add-license-bumf.js dist/astra-db-ts.d.ts +# Delete the "empty" files where only types were declared +node scripts/del-empty-dist-files.js + # Removes all .d.ts files except the main rollup .d.ts cd dist || return 1 find . -type f -name '*.d.ts' ! -name 'astra-db-ts.d.ts' -exec rm {} + cd .. + +# Delete any empty leftover directories +find ./dist -type d -empty -delete diff --git a/scripts/del-empty-dist-files.js b/scripts/del-empty-dist-files.js new file mode 100644 index 00000000..2442ecaa --- /dev/null +++ b/scripts/del-empty-dist-files.js @@ -0,0 +1,43 @@ +const fs = require('fs'); +const path = require('path'); + +const targetContent = `"use strict"; +// Copyright Datastax, Inc +// SPDX-License-Identifier: Apache-2.0 +Object.defineProperty(exports, "__esModule", { value: true }); +`; + +function deleteEmptyFiles(dirPath) { + fs.readdir(dirPath, (err, files) => { + if (err) { + return console.error('Unable to scan directory: ' + err); + } + + files.forEach(file => { + const filePath = path.join(dirPath, file); + + fs.stat(filePath, (err, stat) => { + if (err) { + return console.error('Error stating file: ' + err); + } + + if (stat.isDirectory()) { + deleteEmptyFiles(filePath); + return; + } + + fs.readFile(filePath, 'utf8', (err, data) => { + if (err) { + return console.error('Error reading file: ' + err); + } + + if (data === targetContent) { + fs.unlink(filePath, (err) => err && console.error('Error deleting file: ' + err)); + } + }); + }); + }); + }); +} + +deleteEmptyFiles('./dist'); diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100644 index 00000000..dd82bc0f --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,109 @@ +#!/usr/bin/sh + +# Define necessary commands +test_cmd="npx ts-mocha --paths -p tsconfig.json --recursive tests/prelude.test.ts tests/unit tests/integration --extension .test.ts -t 60000" + +all_tests_cmd="env ASTRA_RUN_LONG_TESTS=1 ASTRA_RUN_ADMIN_TESTS=1 ASTRA_RUN_VECTORIZE_TESTS=1 $test_cmd" + +light_tests_cmd="env ASTRA_RUN_LONG_TESTS=0 ASTRA_RUN_ADMIN_TESTS=0 ASTRA_RUN_VECTORIZE_TESTS=0 $test_cmd" + +run_lint_cmd="npm run lint" + +run_tsc_cmd="npx tsc --noEmit --skipLibCheck" + +# Counter to make sure test type isn't set multiple times +test_type_set=0 + +# Process all of the flags +while [ $# -gt 0 ]; do + for test_type_flag in --all --light --coverage --types --prerelease; do + [ "$1" = "$test_type_flag" ] && test_type_set=$((test_type_set + 1)) + done + + if [ "$test_type_set" -gt 1 ]; then + echo "Can't set multiple of --all, --light, --coverage, --types, and --prerelease" + exit + fi + + case "$1" in + "--all") + test_type="all" + ;; + "--light") + test_type="light" + ;; + "--coverage") + test_type="coverage" + ;; + "--types") + test_type="types" + ;; + "--prerelease") + test_type="prerelease" + ;; + "-f") + shift + filter="$1" + ;; + "-b") + bail_early=1 + ;; + "--args") + shift + raw_args="$1" + ;; + *) + echo "Invalid flag $1" + echo "" + echo "Usage:" + echo "npm run test -- [--all | --light | --coverage | --prerelease] [-f ] [-b] [--args ]" + echo "or" + echo "npm run test -- <--types>" + exit + ;; + esac + shift +done + +# Ensure the flags are compatible with each other +if [ "$test_type" = '--types' ] && { [ -n "$bail_early" ] || [ -n "$filter" ] || [ -n "$raw_args" ]; }; then + echo "Can't use a filter, bail, or args flag when typechecking" + exit +fi + +# Build the actual command to run +case "$test_type" in + "") + cmd_to_run="$test_cmd" + ;; + "all") + cmd_to_run="$all_tests_cmd" + ;; + "light") + cmd_to_run="$light_tests_cmd" + ;; + "coverage") + cmd_to_run="npx nyc $all_tests_cmd -b" + ;; + "types") + cmd_to_run="$run_tsc_cmd" + ;; + "prerelease") + cmd_to_run="$run_lint_cmd && $run_tsc_cmd && $all_tests_cmd -b --exit" + ;; +esac + +if [ -n "$filter" ]; then + cmd_to_run="$cmd_to_run -f '$filter'" +fi + +if [ -n "$bail_early" ]; then + cmd_to_run="$cmd_to_run -b" +fi + +if [ -n "$raw_args" ]; then + cmd_to_run="$cmd_to_run $raw_args" +fi + +# Run it +eval "$cmd_to_run" diff --git a/scripts/version.sh b/scripts/version.sh new file mode 100644 index 00000000..b8561d23 --- /dev/null +++ b/scripts/version.sh @@ -0,0 +1,3 @@ +npm run build +sh scripts/update-example-client-dep.sh npm +git add . diff --git a/src/api/clients/data-api-http-client.ts b/src/api/clients/data-api-http-client.ts index 1828e47e..a2e486d3 100644 --- a/src/api/clients/data-api-http-client.ts +++ b/src/api/clients/data-api-http-client.ts @@ -37,7 +37,7 @@ import { AdminCommandSucceededEvent, AdminSpawnOptions, } from '@/src/devops'; -import { nullish, TokenProvider } from '@/src/common'; +import { isNullish, nullish, TokenProvider } from '@/src/common'; import { EmbeddingHeadersProvider } from '@/src/data-api/embedding-providers'; /** @@ -106,7 +106,7 @@ interface DataAPIHttpClientOpts extends HTTPClientOptions { */ export class DataAPIHttpClient extends HttpClient { public collection?: string; - public namespace?: NamespaceRef; + public namespace: NamespaceRef; public maxTimeMS: number; public emissionStrategy: ReturnType readonly #props: DataAPIHttpClientOpts; @@ -143,7 +143,6 @@ export class DataAPIHttpClient extends HttpClient { clone.emissionStrategy = EmissionStrategy.Admin(this.emitter); clone.collection = undefined; - clone.namespace = undefined; return clone; } @@ -173,6 +172,10 @@ export class DataAPIHttpClient extends HttpClient { if (info.namespace !== null) { info.namespace ||= this.namespace?.ref; + + if (isNullish(info.namespace)) { + throw new Error('Db is missing a required namespace; be sure to set one w/ client.db(..., { namespace }), or db.useNamespace()') + } } const keyspacePath = info.namespace ? `/${info.namespace}` : ''; diff --git a/src/api/index.ts b/src/api/index.ts index 994dce06..d213ef8e 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -15,10 +15,10 @@ export * from './clients/http-client'; export * from './clients/data-api-http-client'; export * from './clients/devops-api-http-client'; -export * from './clients/types'; +export type * from './clients/types'; export * from './fetch/fetch-h2'; export * from './fetch/fetch-native'; -export * from './fetch/types'; +export type * from './fetch/types'; export * from './timeout-managers'; export * from './constants'; -export * from './types'; +export type * from './types'; diff --git a/src/client/index.ts b/src/client/index.ts index fb681734..990b5cd1 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -15,7 +15,7 @@ export * from './data-api-client'; export * from './errors'; -export { +export type { DataAPIClientOptions, Caller, DefaultHttpClientOptions, diff --git a/src/common/index.ts b/src/common/index.ts index 782cec30..a226794d 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -export * from './types'; +export type * from './types'; export * from './utils'; export * from './constants'; export * from './token-providers'; diff --git a/src/common/token-providers/static-token-provider.ts b/src/common/token-providers/static-token-provider.ts index e6c21734..72e2cf01 100644 --- a/src/common/token-providers/static-token-provider.ts +++ b/src/common/token-providers/static-token-provider.ts @@ -22,7 +22,7 @@ import { nullish } from '@/src/common'; * string or null/undefined, which is transformed into a {@link StaticTokenProvider} under the hood. * * @example - * ``` + * ```typescript * const provider = new StaticTokenProvider('token'); * const client = new DataAPIClient(provider); * diff --git a/src/common/token-providers/token-provider.ts b/src/common/token-providers/token-provider.ts index dac34086..edced2c6 100644 --- a/src/common/token-providers/token-provider.ts +++ b/src/common/token-providers/token-provider.ts @@ -28,7 +28,7 @@ import { isNullish, nullish, StaticTokenProvider } from '@/src/common'; * into a {@link StaticTokenProvider} under the hood. * * @example - * ``` + * ```typescript * const provider = new UsernamePasswordTokenProvider('username', 'password'); * const client = new DataAPIClient(provider); * ``` diff --git a/src/common/token-providers/userpass-token-providers.ts b/src/common/token-providers/userpass-token-providers.ts index e781c0a8..f6b1abd8 100644 --- a/src/common/token-providers/userpass-token-providers.ts +++ b/src/common/token-providers/userpass-token-providers.ts @@ -20,7 +20,7 @@ import { TokenProvider } from '@/src/common/token-providers/token-provider'; * Uses the format `Cassandra:b64(username):password(username)` * * @example - * ``` + * ```typescript * const provider = new UsernamePasswordTokenProvider('username', 'password'); * const client = new DataAPIClient(provider, { environment: 'dse' }); * ``` diff --git a/src/data-api/db.ts b/src/data-api/db.ts index 8597227a..9abd6eb7 100644 --- a/src/data-api/db.ts +++ b/src/data-api/db.ts @@ -58,7 +58,8 @@ import { DataAPIDbAdmin } from '@/src/devops/data-api-db-admin'; * Db spawning methods let you pass in the default namespace for the database, which is used for all subsequent db * operations in that object, but each method lets you override the namespace if necessary in its options. * - * {@link DbAdmin.findEmbeddingProviders}typescript + * @example + * ```typescript * const client = new DataAPIClient('AstraCS:...'); * * // Connect to a database using a direct endpoint diff --git a/src/data-api/embedding-providers/aws-embedding-headers-provider.ts b/src/data-api/embedding-providers/aws-embedding-headers-provider.ts index 317621fa..b08ea5bf 100644 --- a/src/data-api/embedding-providers/aws-embedding-headers-provider.ts +++ b/src/data-api/embedding-providers/aws-embedding-headers-provider.ts @@ -21,7 +21,7 @@ import { EmbeddingHeadersProvider } from '@/src/data-api/embedding-providers/emb * Sets the headers `x-embedding-access-id` and `x-embedding-secret-id`. * * @example - * ``` + * ```typescript * const provider = new AWSEmbeddingHeadersProvider('access-key-id', 'secret-access-key'); * const collection = await db.collection('my_coll', { embeddingApiKey: provider }); * ``` diff --git a/src/data-api/embedding-providers/embedding-api-key-header-provider.ts b/src/data-api/embedding-providers/embedding-api-key-header-provider.ts index dfae5880..d2bf4139 100644 --- a/src/data-api/embedding-providers/embedding-api-key-header-provider.ts +++ b/src/data-api/embedding-providers/embedding-api-key-header-provider.ts @@ -22,7 +22,7 @@ import { nullish } from '@/src/common'; * string or null/undefined, which is transformed into an {@link EmbeddingAPIKeyHeaderProvider} under the hood. * * @example - * ``` + * ```typescript * const provider = new EmbeddingAPIKeyHeaderProvider('api-key'); * const collection = await db.collection('my_coll', { embeddingApiKey: provider }); * diff --git a/src/data-api/errors.ts b/src/data-api/errors.ts index f1a0587c..45ebd3fe 100644 --- a/src/data-api/errors.ts +++ b/src/data-api/errors.ts @@ -131,6 +131,8 @@ export abstract class DataAPIError extends Error {} /** * An error thrown on non-2XX status codes from the Data API, such as 4XX or 5XX errors. + * + * @public */ export class DataAPIHttpError extends DataAPIError { /** diff --git a/src/data-api/index.ts b/src/data-api/index.ts index eecc4d7b..a7dead7a 100644 --- a/src/data-api/index.ts +++ b/src/data-api/index.ts @@ -30,6 +30,8 @@ export { BulkWriteError, CursorIsStartedError, UpdateManyError, + CollectionNotFoundError, + DataAPIHttpError, } from './errors'; export * from './events'; export * from './ids'; diff --git a/src/data-api/types/document.ts b/src/data-api/types/document.ts index 315e594d..acefe74e 100644 --- a/src/data-api/types/document.ts +++ b/src/data-api/types/document.ts @@ -27,7 +27,7 @@ export type SomeDoc = Record; * Base type for a document that wishes to leverage raw vector capabilities. * * @example - * ``` + * ```typescript * export interface Idea extends VectorDoc { *   category: string, *   idea: string, @@ -53,7 +53,7 @@ export interface VectorDoc { * Base type for a document that wishes to leverage automatic vectorization (assuming the collection is vectorize-enabled). * * @example - * ``` + * ```typescript * export interface Idea extends VectorizeDoc { *   category: string, * } diff --git a/src/data-api/types/dot-notation.ts b/src/data-api/types/dot-notation.ts index 5ece4453..1bc37c23 100644 --- a/src/data-api/types/dot-notation.ts +++ b/src/data-api/types/dot-notation.ts @@ -24,7 +24,7 @@ import { SomeDoc, UUID, ObjectId } from '@/src/data-api'; * to support that.* * * @example - * ``` + * ```typescript * interface BasicSchema { *   num: number, *   arr: string[], diff --git a/src/data-api/types/filter.ts b/src/data-api/types/filter.ts index 5252252e..0c7522c9 100644 --- a/src/data-api/types/filter.ts +++ b/src/data-api/types/filter.ts @@ -24,7 +24,7 @@ import { IsDate, IsNum } from '@/src/data-api/types/utils'; * This is a more relaxed version of {@link StrictFilter} that doesn't type-check nested fields. * * @example - * ``` + * ```typescript * interface BasicSchema { *   arr: string[], *   num: number, @@ -63,7 +63,7 @@ export type Filter = { * You can use it anywhere by using the `satisfies` keyword, or by creating a temporary const with the StrictFilter type. * * @example - * ``` + * ```typescript * interface BasicSchema { *   arr: string[], *   num: number, diff --git a/src/data-api/types/index.ts b/src/data-api/types/index.ts index a749bd25..26e07946 100644 --- a/src/data-api/types/index.ts +++ b/src/data-api/types/index.ts @@ -12,32 +12,32 @@ // See the License for the specific language governing permissions and // limitations under the License. -export * from './collections/collections-common'; -export * from './collections/collection-options'; -export { CreateCollectionOptions } from './collections/create-collection'; -export { ListCollectionsOptions, FullCollectionInfo } from './collections/list-collection'; -export * from './collections/drop-collection'; -export * from './collections/command'; -export * from './collections/spawn-collection'; -export * from './misc/spawn-db'; -export { DeleteManyResult } from './delete/delete-many'; -export { DeleteOneResult, DeleteOneOptions } from './delete/delete-one'; -export { FindOptions } from './find/find'; -export * from './find/find-common'; -export { FindOneOptions } from './find/find-one'; -export { FindOneAndDeleteOptions } from './find/find-one-delete'; -export { FindOneAndReplaceOptions } from './find/find-one-replace'; -export { FindOneAndUpdateOptions } from './find/find-one-update'; -export { InsertManyResult, InsertManyOptions, InsertManyOrderedOptions, InsertManyUnorderedOptions } from './insert/insert-many'; -export { InsertOneOptions, InsertOneResult } from './insert/insert-one'; +export type * from './collections/collections-common'; +export type * from './collections/collection-options'; +export type { CreateCollectionOptions } from './collections/create-collection'; +export type { ListCollectionsOptions, FullCollectionInfo } from './collections/list-collection'; +export type * from './collections/drop-collection'; +export type * from './collections/command'; +export type * from './collections/spawn-collection'; +export type * from './misc/spawn-db'; +export type { DeleteManyResult } from './delete/delete-many'; +export type { DeleteOneResult, DeleteOneOptions } from './delete/delete-one'; +export type { FindOptions } from './find/find'; +export type * from './find/find-common'; +export type { FindOneOptions } from './find/find-one'; +export type { FindOneAndDeleteOptions } from './find/find-one-delete'; +export type { FindOneAndReplaceOptions } from './find/find-one-replace'; +export type { FindOneAndUpdateOptions } from './find/find-one-update'; +export type { InsertManyResult, InsertManyOptions, InsertManyOrderedOptions, InsertManyUnorderedOptions } from './insert/insert-many'; +export type { InsertOneOptions, InsertOneResult } from './insert/insert-one'; +export type * from './update/update-common'; +export type { UpdateManyResult, UpdateManyOptions } from './update/update-many'; +export type { UpdateOneOptions, UpdateOneResult } from './update/update-one'; +export type * from './update/replace-one'; +export type * from './common'; +export type * from './dot-notation'; +export type * from './filter'; +export type * from './update-filter'; +export type * from './document'; +export type { WithId, MaybeId, FoundDoc, NoId, Flatten, IdOf } from './utils'; export * from './misc/bulk-write'; -export * from './update/update-common'; -export { UpdateManyResult, UpdateManyOptions } from './update/update-many'; -export { UpdateOneOptions, UpdateOneResult } from './update/update-one'; -export * from './update/replace-one'; -export * from './common'; -export * from './dot-notation'; -export * from './filter'; -export * from './update-filter'; -export * from './document'; -export { WithId, MaybeId, FoundDoc, NoId, Flatten, IdOf } from './utils'; diff --git a/src/data-api/types/utils.ts b/src/data-api/types/utils.ts index 6cda104c..d1c6f597 100644 --- a/src/data-api/types/utils.ts +++ b/src/data-api/types/utils.ts @@ -19,7 +19,7 @@ import { SomeId } from '@/src/data-api'; * Checks if a type can possibly be some number * * @example - * ``` + * ```typescript * IsNum === true * ``` * @@ -31,7 +31,7 @@ export type IsNum = number extends T ? true : bigint extends T ? true : false * Checks if a type can possibly be a date * * @example - * ``` + * ```typescript * IsDate === boolean * ``` * diff --git a/src/data-api/utils.ts b/src/data-api/utils.ts index 29fcc7c6..e3baee97 100644 --- a/src/data-api/utils.ts +++ b/src/data-api/utils.ts @@ -22,7 +22,7 @@ declare const __error: unique symbol; * More inflexable type than `never`, and gives contextual error messages. * * @example - * ``` + * ```typescript * function unsupported(): TypeErr<'Unsupported operation'> { *   throw new Error('Unsupported operation'); * } diff --git a/src/devops/data-api-db-admin.ts b/src/devops/data-api-db-admin.ts index db87101f..e0a03b9f 100644 --- a/src/devops/data-api-db-admin.ts +++ b/src/devops/data-api-db-admin.ts @@ -131,7 +131,7 @@ export class DataAPIDbAdmin extends DbAdmin { * @returns A promise that resolves to list of all the namespaces in the database. */ public override async listNamespaces(options?: WithTimeout): Promise { - const resp = await this.#httpClient.executeCommand({ findNamespaces: {} }, { maxTimeMS: options?.maxTimeMS }); + const resp = await this.#httpClient.executeCommand({ findNamespaces: {} }, { maxTimeMS: options?.maxTimeMS, namespace: null }); return resp.status!.namespaces; } @@ -171,7 +171,7 @@ export class DataAPIDbAdmin extends DbAdmin { replicationFactor: 1, }; - await this.#httpClient.executeCommand({ createNamespace: { name: namespace, options: { replication } } }, { maxTimeMS: options?.maxTimeMS }); + await this.#httpClient.executeCommand({ createNamespace: { name: namespace, options: { replication } } }, { maxTimeMS: options?.maxTimeMS, namespace: null }); if (options?.updateDbNamespace) { this.#db.useNamespace(namespace); @@ -200,7 +200,7 @@ export class DataAPIDbAdmin extends DbAdmin { * @returns A promise that resolves when the operation completes. */ public override async dropNamespace(namespace: string, options?: AdminBlockingOptions): Promise { - await this.#httpClient.executeCommand({ dropNamespace: { name: namespace } }, { maxTimeMS: options?.maxTimeMS }); + await this.#httpClient.executeCommand({ dropNamespace: { name: namespace } }, { maxTimeMS: options?.maxTimeMS, namespace: null }); } private get _httpClient() { diff --git a/src/devops/index.ts b/src/devops/index.ts index e415d5c6..0e09e98a 100644 --- a/src/devops/index.ts +++ b/src/devops/index.ts @@ -19,4 +19,4 @@ export * from './data-api-db-admin'; export * from './db-admin'; export * from './errors'; export * from './events'; -export * from './types'; +export type * from './types'; diff --git a/src/devops/types/admin/admin-common.ts b/src/devops/types/admin/admin-common.ts index caf10590..77a1a77e 100644 --- a/src/devops/types/admin/admin-common.ts +++ b/src/devops/types/admin/admin-common.ts @@ -55,7 +55,7 @@ export type DatabaseAction = 'park' | 'unpark' | 'resize' | 'resetPassword' | 'a * to determine when the operation is complete. * * @example - * ``` + * ```typescript * // Will block by default until the operation is complete. * const dbAdmin1 = await admin.createDatabase({...}); * diff --git a/src/devops/types/db-admin/find-embedding-providers.ts b/src/devops/types/db-admin/find-embedding-providers.ts index 232fd0fb..9e25fc29 100644 --- a/src/devops/types/db-admin/find-embedding-providers.ts +++ b/src/devops/types/db-admin/find-embedding-providers.ts @@ -98,7 +98,7 @@ export interface EmbeddingProviderInfo { * }); * ``` * - * - `SHARED_SECRET`: Authentication tied to a collection @ collection creation time using the Astra KMS. + * - `SHARED_SECRET`: Authentication tied to a collection at collection creation time using the Astra KMS. * ```typescript * const collection = await db.collection('my_coll', { *   // Not tied to the collection; can be different every time. diff --git a/src/devops/types/index.ts b/src/devops/types/index.ts index 6624fd45..cf81e678 100644 --- a/src/devops/types/index.ts +++ b/src/devops/types/index.ts @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -export * from './admin/admin-common'; -export * from './admin/create-database'; -export * from './admin/database-info'; -export * from './admin/list-databases'; -export * from './admin/spawn-admin'; -export * from './db-admin/create-namespace'; -export * from './db-admin/local-create-namespace'; +export type * from './admin/admin-common'; +export type * from './admin/create-database'; +export type * from './admin/database-info'; +export type * from './admin/list-databases'; +export type * from './admin/spawn-admin'; +export type * from './db-admin/create-namespace'; +export type * from './db-admin/local-create-namespace'; +export type * from './db-admin/find-embedding-providers'; diff --git a/tests/fixtures.ts b/tests/fixtures.ts index 99df1727..e87cd027 100644 --- a/tests/fixtures.ts +++ b/tests/fixtures.ts @@ -28,8 +28,6 @@ export const EPHEMERAL_COLLECTION_NAME = 'temp_coll'; export const OTHER_NAMESPACE = 'other_keyspace'; export const TEMP_DB_NAME = 'astra-test-db-plus-random-name-1284' -let collsSetup = false; - export const USE_HTTP2 = !process.env.ASTRA_USE_HTTP1; export const HTTP_CLIENT_TYPE = process.env.ASTRA_USE_FETCH ? 'fetch' : undefined; @@ -42,32 +40,22 @@ export const TEST_APPLICATION_URI = process.env.APPLICATION_URI; export const DEMO_APPLICATION_URI = 'https://12341234-1234-1234-1234-123412341234-us-west-2.apps.astra-dev.datastax.com'; export const ENVIRONMENT = (process.env.APPLICATION_ENVIRONMENT ?? 'astra') as DataAPIEnvironment; -export const initTestObjects = async (ctx: Context, preferHttp2 = USE_HTTP2, clientType: typeof HTTP_CLIENT_TYPE = HTTP_CLIENT_TYPE): Promise<[DataAPIClient, Db, Collection]> => { +export const initTestObjects = async (preferHttp2 = USE_HTTP2, environment: typeof ENVIRONMENT = ENVIRONMENT): Promise<[DataAPIClient, Db, Collection]> => { const client = new DataAPIClient(TEST_APPLICATION_TOKEN, { - httpOptions: { preferHttp2, client: clientType }, + httpOptions: { preferHttp2, client: HTTP_CLIENT_TYPE }, dbOptions: { namespace: DEFAULT_NAMESPACE }, - environment: ENVIRONMENT, + environment: environment, }); const db = client.db(TEST_APPLICATION_URI); - if (!collsSetup) { - await db.dropCollection(EPHEMERAL_COLLECTION_NAME); - await db.dropCollection(EPHEMERAL_COLLECTION_NAME, { namespace: OTHER_NAMESPACE }); - await db.createCollection(DEFAULT_COLLECTION_NAME, { vector: { dimension: 5, metric: 'cosine' }, checkExists: false, namespace: OTHER_NAMESPACE }); - await db.createCollection(DEFAULT_COLLECTION_NAME, { vector: { dimension: 5, metric: 'cosine' }, checkExists: false }); - - collsSetup = true; - } - const collection = db.collection(DEFAULT_COLLECTION_NAME); - await collection.deleteMany({}); return [client, db, collection]; }; -export const initCollectionWithFailingClient = async (ctx: Context) => { - const [, , collection] = await initTestObjects(ctx); +export const initCollectionWithFailingClient = async () => { + const [, , collection] = await initTestObjects(); collection['_httpClient'].executeCommand = () => { throw new Error('test') }; return collection; } diff --git a/tests/integration/api/data-api-http-client.test.ts b/tests/integration/api/data-api-http-client.test.ts index f87a7651..78f3d12e 100644 --- a/tests/integration/api/data-api-http-client.test.ts +++ b/tests/integration/api/data-api-http-client.test.ts @@ -24,7 +24,7 @@ describe('integration.api.data-api-http-client', () => { before(async function () { let db: Db; - [, db, collection] = await initTestObjects(this); + [, db, collection] = await initTestObjects(); httpClient = db['_httpClient']; }); @@ -59,7 +59,7 @@ describe('integration.api.data-api-http-client', () => { }); it('should error on invalid token', async function () { - const [client] = await initTestObjects(this); + const [client] = await initTestObjects(); const httpClient = client.db(TEST_APPLICATION_URI, { token: 'invalid-token' })['_httpClient']; try { diff --git a/tests/integration/client/data-api-client.test.ts b/tests/integration/client/data-api-client.test.ts index c15b13ad..38f3de10 100644 --- a/tests/integration/client/data-api-client.test.ts +++ b/tests/integration/client/data-api-client.test.ts @@ -48,7 +48,7 @@ describe('integration.client.data-api-client', () => { }); it('lets Data API deal with throwing missing token error', async () => { - const db = new DataAPIClient({ environment: ENVIRONMENT }).db(TEST_APPLICATION_URI); + const db = new DataAPIClient({ environment: ENVIRONMENT }).db(TEST_APPLICATION_URI, { namespace: DEFAULT_NAMESPACE }); await assert.rejects(() => db.listCollections(), { message: 'Role unauthorized for operation: Missing token, expecting one in the Token header.' }); }); }); @@ -92,7 +92,7 @@ describe('integration.client.data-api-client', () => { let db: Db; before(async function () { - [, db] = await initTestObjects(this); + [, db] = await initTestObjects(); }); beforeEach(async () => { diff --git a/tests/integration/data-api/collection/bulk-write.test.ts b/tests/integration/data-api/collection/bulk-write.test.ts index 66cb9d4d..de470a62 100644 --- a/tests/integration/data-api/collection/bulk-write.test.ts +++ b/tests/integration/data-api/collection/bulk-write.test.ts @@ -21,7 +21,7 @@ describe('integration.data-api.collection.bulk-write', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); beforeEach(async () => { @@ -177,7 +177,7 @@ describe('integration.data-api.collection.bulk-write', () => { }); it('fails fast on hard errors ordered', async function () { - const collection = await initCollectionWithFailingClient(this); + const collection = await initCollectionWithFailingClient(); try { await collection.bulkWrite([{ insertOne: { document: { _id: 'a' } } }], { ordered: true }); assert.fail('Expected an error'); @@ -189,7 +189,7 @@ describe('integration.data-api.collection.bulk-write', () => { }); it('fails fast on hard errors unordered', async function () { - const collection = await initCollectionWithFailingClient(this); + const collection = await initCollectionWithFailingClient(); try { await collection.bulkWrite([{ insertOne: { document: { _id: 'a' } } }], { ordered: false }); assert.fail('Expected an error'); diff --git a/tests/integration/data-api/collection/count-documents.test.ts b/tests/integration/data-api/collection/count-documents.test.ts index 90a859a8..7f2744ce 100644 --- a/tests/integration/data-api/collection/count-documents.test.ts +++ b/tests/integration/data-api/collection/count-documents.test.ts @@ -21,7 +21,7 @@ describe('integration.data-api.collection.count-documents', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); beforeEach(async () => { diff --git a/tests/integration/data-api/collection/delete-all.test.ts b/tests/integration/data-api/collection/delete-all.test.ts index f66b477f..c8d24afb 100644 --- a/tests/integration/data-api/collection/delete-all.test.ts +++ b/tests/integration/data-api/collection/delete-all.test.ts @@ -20,7 +20,7 @@ describe('integration.data-api.collection.delete-all', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); beforeEach(async () => { diff --git a/tests/integration/data-api/collection/delete-many.test.ts b/tests/integration/data-api/collection/delete-many.test.ts index 47ccbdd3..3eb816ff 100644 --- a/tests/integration/data-api/collection/delete-many.test.ts +++ b/tests/integration/data-api/collection/delete-many.test.ts @@ -21,7 +21,7 @@ describe('integration.data-api.collection.delete-many', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); beforeEach(async () => { @@ -72,7 +72,7 @@ describe('integration.data-api.collection.delete-many', () => { }); it('fails fast on hard errors', async function () { - const collection = await initCollectionWithFailingClient(this); + const collection = await initCollectionWithFailingClient(); try { await collection.deleteMany({ _id: 3 }); assert.fail('Expected an error'); diff --git a/tests/integration/data-api/collection/delete-one.test.ts b/tests/integration/data-api/collection/delete-one.test.ts index 65897118..6e9e975c 100644 --- a/tests/integration/data-api/collection/delete-one.test.ts +++ b/tests/integration/data-api/collection/delete-one.test.ts @@ -20,7 +20,7 @@ describe('integration.data-api.collection.delete-one', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); beforeEach(async () => { diff --git a/tests/integration/data-api/collection/distinct.test.ts b/tests/integration/data-api/collection/distinct.test.ts index f1467878..e82a60cb 100644 --- a/tests/integration/data-api/collection/distinct.test.ts +++ b/tests/integration/data-api/collection/distinct.test.ts @@ -20,7 +20,7 @@ describe('integration.data-api.collection.distinct', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); beforeEach(async () => { diff --git a/tests/integration/data-api/collection/drop.test.ts b/tests/integration/data-api/collection/drop.test.ts index 2a8cd0d6..8251c29b 100644 --- a/tests/integration/data-api/collection/drop.test.ts +++ b/tests/integration/data-api/collection/drop.test.ts @@ -20,7 +20,7 @@ describe('integration.data-api.collection.drop', () => { let db: Db; before(async function () { - [, db] = await initTestObjects(this); + [, db] = await initTestObjects(); }); it('[long] drops itself', async function () { diff --git a/tests/integration/data-api/collection/estimated-document-count.test.ts b/tests/integration/data-api/collection/estimated-document-count.test.ts index 23e0b37f..8adf4427 100644 --- a/tests/integration/data-api/collection/estimated-document-count.test.ts +++ b/tests/integration/data-api/collection/estimated-document-count.test.ts @@ -21,7 +21,7 @@ describe('integration.data-api.collection.estimated-document-count', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); it('roughly works', async () => { diff --git a/tests/integration/data-api/collection/find-one-and-delete.test.ts b/tests/integration/data-api/collection/find-one-and-delete.test.ts index 99ab37ef..7fccee22 100644 --- a/tests/integration/data-api/collection/find-one-and-delete.test.ts +++ b/tests/integration/data-api/collection/find-one-and-delete.test.ts @@ -20,7 +20,7 @@ describe('integration.data-api.collection.find-one-and-delete', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); beforeEach(async () => { diff --git a/tests/integration/data-api/collection/find-one-and-replace.test.ts b/tests/integration/data-api/collection/find-one-and-replace.test.ts index 690d57f4..5dd3f295 100644 --- a/tests/integration/data-api/collection/find-one-and-replace.test.ts +++ b/tests/integration/data-api/collection/find-one-and-replace.test.ts @@ -20,7 +20,7 @@ describe('integration.data-api.collection.find-one-and-replace', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); beforeEach(async () => { diff --git a/tests/integration/data-api/collection/find-one-and-update.test.ts b/tests/integration/data-api/collection/find-one-and-update.test.ts index 71d02ec4..082a6f1d 100644 --- a/tests/integration/data-api/collection/find-one-and-update.test.ts +++ b/tests/integration/data-api/collection/find-one-and-update.test.ts @@ -20,7 +20,7 @@ describe('integration.data-api.collection.find-one-and-update', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); beforeEach(async () => { diff --git a/tests/integration/data-api/collection/finds.test.ts b/tests/integration/data-api/collection/finds.test.ts index 4eba5095..7ab603d8 100644 --- a/tests/integration/data-api/collection/finds.test.ts +++ b/tests/integration/data-api/collection/finds.test.ts @@ -28,7 +28,7 @@ describe('integration.data-api.collection.finds', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); beforeEach(async () => { diff --git a/tests/integration/data-api/collection/insert-many.test.ts b/tests/integration/data-api/collection/insert-many.test.ts index 1e40bacf..52522916 100644 --- a/tests/integration/data-api/collection/insert-many.test.ts +++ b/tests/integration/data-api/collection/insert-many.test.ts @@ -21,7 +21,7 @@ describe('integration.data-api.collection.insert-many', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); beforeEach(async () => { @@ -207,7 +207,7 @@ describe('integration.data-api.collection.insert-many', () => { }); it('fails fast on hard errors ordered', async function () { - const collection = await initCollectionWithFailingClient(this); + const collection = await initCollectionWithFailingClient(); try { await collection.insertMany([{ name: 'Ignea' }], { ordered: true }); assert.fail('Expected an error'); @@ -219,7 +219,7 @@ describe('integration.data-api.collection.insert-many', () => { }); it('fails fast on hard errors unordered', async function () { - const collection = await initCollectionWithFailingClient(this); + const collection = await initCollectionWithFailingClient(); try { await collection.insertMany([{ name: 'Ignea' }], { ordered: false }); assert.fail('Expected an error'); diff --git a/tests/integration/data-api/collection/insert-one.test.ts b/tests/integration/data-api/collection/insert-one.test.ts index a7367b47..2ad419ca 100644 --- a/tests/integration/data-api/collection/insert-one.test.ts +++ b/tests/integration/data-api/collection/insert-one.test.ts @@ -21,7 +21,7 @@ describe('integration.data-api.collection.insert-one', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); beforeEach(async () => { diff --git a/tests/integration/data-api/collection/misc.test.ts b/tests/integration/data-api/collection/misc.test.ts index dede9a29..47c0a34a 100644 --- a/tests/integration/data-api/collection/misc.test.ts +++ b/tests/integration/data-api/collection/misc.test.ts @@ -23,7 +23,7 @@ describe('integration.data-api.collection.misc', () => { let collection: Collection; before(async function () { - [, db, collection] = await initTestObjects(this); + [, db, collection] = await initTestObjects(); }); describe('initialization', () => { @@ -45,7 +45,7 @@ describe('integration.data-api.collection.misc', () => { describe('timeout', () => { it('times out on http2', async function () { - const [, newDb] = await initTestObjects(this, true); + const [, newDb] = await initTestObjects(true); try { await newDb.collection(DEFAULT_COLLECTION_NAME).insertOne({ username: 'test' }, { maxTimeMS: 10 }); @@ -56,7 +56,7 @@ describe('integration.data-api.collection.misc', () => { }); it('times out on http1', async function () { - const [, newDb] = await initTestObjects(this, false); + const [, newDb] = await initTestObjects(false); try { await newDb.collection(DEFAULT_COLLECTION_NAME).insertOne({ username: 'test' }, { maxTimeMS: 10 }); diff --git a/tests/integration/data-api/collection/options.test.ts b/tests/integration/data-api/collection/options.test.ts index 0cc70791..50f283d1 100644 --- a/tests/integration/data-api/collection/options.test.ts +++ b/tests/integration/data-api/collection/options.test.ts @@ -20,7 +20,7 @@ describe('integration.data-api.collection.options', () => { let db: Db; before(async function () { - [, db] = await initTestObjects(this); + [, db] = await initTestObjects(); await db.dropCollection('test_db_collection_empty_opts'); }); diff --git a/tests/integration/data-api/collection/replace-one.test.ts b/tests/integration/data-api/collection/replace-one.test.ts index 8336667c..608e537e 100644 --- a/tests/integration/data-api/collection/replace-one.test.ts +++ b/tests/integration/data-api/collection/replace-one.test.ts @@ -21,7 +21,7 @@ describe('integration.data-api.collection.replace-one', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); beforeEach(async () => { diff --git a/tests/integration/data-api/collection/update-many.test.ts b/tests/integration/data-api/collection/update-many.test.ts index dfd4b325..7ca6dd35 100644 --- a/tests/integration/data-api/collection/update-many.test.ts +++ b/tests/integration/data-api/collection/update-many.test.ts @@ -21,7 +21,7 @@ describe('integration.data-api.collection.update-many', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); beforeEach(async () => { @@ -879,7 +879,7 @@ describe('integration.data-api.collection.update-many', () => { }); it('fails fast on hard errors', async function () { - const collection = await initCollectionWithFailingClient(this); + const collection = await initCollectionWithFailingClient(); try { await collection.updateMany({}, {}); assert.fail('Expected an error'); diff --git a/tests/integration/data-api/collection/update-one.test.ts b/tests/integration/data-api/collection/update-one.test.ts index 145a5c8c..97acb989 100644 --- a/tests/integration/data-api/collection/update-one.test.ts +++ b/tests/integration/data-api/collection/update-one.test.ts @@ -20,7 +20,7 @@ describe('integration.data-api.collection.update-one', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); beforeEach(async () => { diff --git a/tests/integration/data-api/cursor.test.ts b/tests/integration/data-api/cursor.test.ts index c4a48e23..edc2f21c 100644 --- a/tests/integration/data-api/cursor.test.ts +++ b/tests/integration/data-api/cursor.test.ts @@ -28,7 +28,7 @@ describe('integration.data-api.cursor', () => { const ageToString = (doc: SomeDoc) => ({ age: `${doc.age}` }); before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); httpClient = collection['_httpClient']; }); diff --git a/tests/integration/data-api/db.test.ts b/tests/integration/data-api/db.test.ts index 04dabb05..330355a3 100644 --- a/tests/integration/data-api/db.test.ts +++ b/tests/integration/data-api/db.test.ts @@ -32,7 +32,7 @@ describe('integration.data-api.db', () => { let db: Db; before(async function () { - [, db] = await initTestObjects(this); + [, db] = await initTestObjects(); await db.dropCollection(EPHEMERAL_COLLECTION_NAME); await db.dropCollection(EPHEMERAL_COLLECTION_NAME, { namespace: OTHER_NAMESPACE }); }); @@ -207,6 +207,10 @@ describe('integration.data-api.db', () => { }); describe('command', () => { + beforeEach(async () => { + await db.collection(DEFAULT_COLLECTION_NAME).deleteMany({}); + }); + afterEach(async function () { await db.dropCollection(EPHEMERAL_COLLECTION_NAME); await db.dropCollection(EPHEMERAL_COLLECTION_NAME, { namespace: OTHER_NAMESPACE }); @@ -246,5 +250,17 @@ describe('integration.data-api.db', () => { assert.ok(e instanceof CollectionNotFoundError); } }); + + it('should throw an error if no namespace set', async () => { + const [, db] = await initTestObjects(); + db.useNamespace(undefined!); + await assert.rejects(() => db.command({ findEmbeddingProviders: {} }), { message: 'Db is missing a required namespace; be sure to set one w/ client.db(..., { namespace }), or db.useNamespace()' }); + }); + + it('should not throw an error if no namespace set but namespace: null', async () => { + const [, db] = await initTestObjects(); + db.useNamespace(undefined!); + await assert.doesNotReject(() => db.command({ findEmbeddingProviders: {} }, { namespace: null })); + }); }); }); diff --git a/tests/integration/data-api/ids.test.ts b/tests/integration/data-api/ids.test.ts index cb478e36..34cd1b6d 100644 --- a/tests/integration/data-api/ids.test.ts +++ b/tests/integration/data-api/ids.test.ts @@ -26,7 +26,7 @@ describe('integration.data-api.ids', () => { let db: Db; before(async function() { - [, db] = await initTestObjects(this); + [, db] = await initTestObjects(); }); describe('default', () => { diff --git a/tests/integration/data-api/vectorize.test.ts b/tests/integration/data-api/vectorize.test.ts index 304fe57f..78089545 100644 --- a/tests/integration/data-api/vectorize.test.ts +++ b/tests/integration/data-api/vectorize.test.ts @@ -13,7 +13,12 @@ // limitations under the License. import assert from 'assert'; -import { Db } from '@/src/data-api'; +import { + AWSEmbeddingHeadersProvider, + Db, + EmbeddingAPIKeyHeaderProvider, + EmbeddingHeadersProvider, +} from '@/src/data-api'; import { assertTestsEnabled, ENVIRONMENT, initTestObjects } from '@/tests/fixtures'; import * as fs from 'fs'; import { after } from 'mocha'; @@ -22,9 +27,9 @@ import { EmbeddingProviderModelInfo, } from '@/src/devops/types/db-admin/find-embedding-providers'; -interface VectorizeTestSpec { +type VectorizeTestSpec = { [providerName: string]: { - apiKey?: string, + [header: `x-${string}`]: string, providerKey?: string, dimension?: { [modelNameRegex: string]: number, @@ -35,13 +40,13 @@ interface VectorizeTestSpec { } } -describe('[astra] integration.data-api.vectorize', () => { +describe('integration.data-api.vectorize', () => { let db: Db; before(async function () { - assertTestsEnabled(this, 'VECTORIZE', 'LONG', 'ASTRA'); + assertTestsEnabled(this, 'VECTORIZE', 'LONG'); - [, db] = await initTestObjects(this); + [, db] = await initTestObjects(); const tests: VectorizeTest[] = await initVectorTests(db).catch((e: unknown) => { console.error('Failed to initialize vectorize tests', e); @@ -92,7 +97,6 @@ const initVectorTests = async (db: Db) => { return Object.entries(embeddingProviders) .flatMap(branchOnModel(spec)) .filter(t => { - console.log(t.testName); return whitelist.test(t.testName); }); }; @@ -142,23 +146,25 @@ const addParameters = (spec: VectorizeTestSpec[string], providerInfo: EmbeddingP interface AuthBranch extends WithParams { authType: 'header' | 'providerKey' | 'none', + header?: EmbeddingHeadersProvider, providerKey?: string, - header?: string, } const branchOnAuth = (spec: VectorizeTestSpec[string], providerInfo: EmbeddingProviderInfo) => (test: WithParams): AuthBranch[] => { const auth = providerInfo['supportedAuthentication']; const tests: AuthBranch[] = [] - if (auth['HEADER'].enabled && spec.apiKey) { - tests.push({ ...test, authType: 'header', header: spec.apiKey, testName: `${test.testName}@header` }); + const ehp = resolveHeaderProvider(spec); + + if (auth['HEADER'].enabled && ehp) { + tests.push({ ...test, authType: 'header', header: ehp, testName: `${test.testName}@header` }); } - if (auth['SHARED_SECRET'].enabled && spec.providerKey) { + if (auth['SHARED_SECRET'].enabled && spec.providerKey && ENVIRONMENT === 'astra') { tests.push({ ...test, authType: 'providerKey', providerKey: spec.providerKey, testName: `${test.testName}@providerKey` }); } - if (auth['NONE'].enabled) { + if (auth['NONE'].enabled && ENVIRONMENT === 'astra') { tests.push({ ...test, authType: 'none', testName: `${test.testName}@none` }); } @@ -167,6 +173,24 @@ const branchOnAuth = (spec: VectorizeTestSpec[string], providerInfo: EmbeddingPr return tests.flatMap(branchOnDimension(spec, modelInfo)); } +const resolveHeaderProvider = (spec: VectorizeTestSpec[string]) => { + const headers = Object.entries(spec).filter(([k]) => k.startsWith('x-')).sort() as [string, string][]; + + if (headers.length === 0) { + return null; + } + + if (headers.length === 1 && headers[0][0] === 'x-embedding-api-key') { + return new EmbeddingAPIKeyHeaderProvider(headers[0][1]); + } + + if (headers.length === 2 && headers[0][0] === 'x-embedding-access-id' && headers[1][0] === 'x-embedding-secret-id') { + return new AWSEmbeddingHeadersProvider(headers[0][1], headers[1][1]); + } + + throw new Error(`No embeddings header provider resolved for headers ${headers}`); +} + interface DimensionBranch extends AuthBranch { dimension?: number, } @@ -261,7 +285,7 @@ const createVectorizeProvidersTest = (db: Db, test: VectorizeTest, name: string) }).toArray(); assert.strictEqual(findResult.length, 2); - }).timeout(90000); + }); }; const createVectorizeParamTests = function (db: Db, test: VectorizeTest, name: string) { diff --git a/tests/integration/devops/db-admin.test.ts b/tests/integration/devops/db-admin.test.ts index d7a5d877..62277e96 100644 --- a/tests/integration/devops/db-admin.test.ts +++ b/tests/integration/devops/db-admin.test.ts @@ -24,7 +24,7 @@ describe('integration.devops.db-admin', () => { let client: DataAPIClient; before(async function () { - [client, db] = await initTestObjects(this); + [client, db] = await initTestObjects(); }); it('[long] [not-dev] works', async function () { diff --git a/tests/integration/devops/lifecycle.test.ts b/tests/integration/devops/lifecycle.test.ts index 35c33099..26bf5103 100644 --- a/tests/integration/devops/lifecycle.test.ts +++ b/tests/integration/devops/lifecycle.test.ts @@ -26,7 +26,7 @@ describe('[admin] [long] [not-dev] integration.devops.lifecycle', () => { before(async function () { assertTestsEnabled(this, 'ADMIN', 'LONG', 'NOT-DEV', 'ASTRA'); - [client] = await initTestObjects(this); + [client] = await initTestObjects(); for (const db of await client.admin().listDatabases()) { if (db.info.name === TEMP_DB_NAME && db.status !== 'TERMINATING') { diff --git a/tests/integration/misc/code-samples.test.ts b/tests/integration/misc/code-samples.test.ts index 97685a0e..b19afba0 100644 --- a/tests/integration/misc/code-samples.test.ts +++ b/tests/integration/misc/code-samples.test.ts @@ -32,7 +32,7 @@ describe('integration.misc.code-samples', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); beforeEach(async function () { diff --git a/tests/integration/misc/headers.test.ts b/tests/integration/misc/headers.test.ts index 04965fa6..c558d1b3 100644 --- a/tests/integration/misc/headers.test.ts +++ b/tests/integration/misc/headers.test.ts @@ -88,7 +88,7 @@ describe('integration.misc.headers', () => { let collection: Collection; before(async function () { - [, , collection] = await initTestObjects(this); + [, , collection] = await initTestObjects(); }); beforeEach(async function () { diff --git a/tests/integration/misc/heirarchy-traversal.test.ts b/tests/integration/misc/heirarchy-traversal.test.ts index 9c0d4f63..5c450c54 100644 --- a/tests/integration/misc/heirarchy-traversal.test.ts +++ b/tests/integration/misc/heirarchy-traversal.test.ts @@ -30,7 +30,7 @@ describe('[astra] integration.misc.hierarchy-traversal', () => { before(async function () { assertTestsEnabled(this, 'ASTRA'); - [client, db, collection] = await initTestObjects(this); + [client, db, collection] = await initTestObjects(); const idAndRegion = endpoint.split('.')[0].split('https://')[1].split('-'); diff --git a/tests/integration/misc/quickstart.test.ts b/tests/integration/misc/quickstart.test.ts index 8346da6b..6a6b2449 100644 --- a/tests/integration/misc/quickstart.test.ts +++ b/tests/integration/misc/quickstart.test.ts @@ -29,7 +29,7 @@ describe('integration.misc.quickstart', () => { let db: Db; before(async function () { - [, db] = await initTestObjects(this); + [, db] = await initTestObjects(); }); describe('[long] quickstart', () => { diff --git a/tests/integration/misc/timeouts.test.ts b/tests/integration/misc/timeouts.test.ts index 15b6a59c..53170b22 100644 --- a/tests/integration/misc/timeouts.test.ts +++ b/tests/integration/misc/timeouts.test.ts @@ -32,7 +32,7 @@ describe('integration.misc.timeouts', () => { let collection: Collection; before(async function () { - [, db, collection] = await initTestObjects(this); + [, db, collection] = await initTestObjects(); }); describe('in data-api', () => { diff --git a/tests/prelude.test.ts b/tests/prelude.test.ts new file mode 100644 index 00000000..29fe5bdc --- /dev/null +++ b/tests/prelude.test.ts @@ -0,0 +1,25 @@ +// Copyright DataStax, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { DEFAULT_COLLECTION_NAME, initTestObjects, OTHER_NAMESPACE } from '@/tests/fixtures'; + +before(async () => { + const [, db] = await initTestObjects(); + + await db.createCollection(DEFAULT_COLLECTION_NAME, { vector: { dimension: 5, metric: 'cosine' }, checkExists: false, namespace: OTHER_NAMESPACE }) + .then(c => c.deleteMany({})); + + await db.createCollection(DEFAULT_COLLECTION_NAME, { vector: { dimension: 5, metric: 'cosine' }, checkExists: false }) + .then(c => c.deleteMany({})); +}); diff --git a/tests/setup.ts b/tests/setup.ts index 99adc58a..dd508e72 100644 --- a/tests/setup.ts +++ b/tests/setup.ts @@ -12,6 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -import dotenv from "dotenv"; +import dotenv from 'dotenv'; dotenv.config();