Skip to content

Commit ddf6d9c

Browse files
committed
more docs, more http2 methods, more tests
1 parent 16e1659 commit ddf6d9c

File tree

16 files changed

+568
-89
lines changed

16 files changed

+568
-89
lines changed

src/api/http-client.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,11 @@ import { HTTP1AuthHeaderFactories, HTTP1Strategy } from '@/src/api/http1';
2525
import { HTTP2Strategy } from '@/src/api/http2';
2626
import { Mutable } from '@/src/data-api/types/utils';
2727

28-
export const applicationTokenKey = Symbol('applicationToken');
29-
3028
export class HttpClient {
3129
public readonly baseUrl: string;
3230
public readonly logSkippedOptions: boolean;
3331
public readonly userAgent: string;
3432
public requestStrategy: HTTPRequestStrategy;
35-
// private [applicationTokenKey]!: string;
3633
#applicationToken: string;
3734

3835
constructor(options: HTTPClientOptions) {
@@ -48,11 +45,6 @@ export class HttpClient {
4845
throw new Error("applicationToken required for initialization");
4946
}
5047

51-
// Object.defineProperty(this, applicationTokenKey, {
52-
// value: options.applicationToken,
53-
// enumerable: false,
54-
// writable: true,
55-
// });
5648
this.#applicationToken = options.applicationToken;
5749

5850
this.baseUrl = options.baseUrl;

src/api/http2.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ import * as http2 from 'http2';
1616
import { HTTPRequestStrategy, GuaranteedAPIResponse, InternalHTTPRequestInfo } from '@/src/api/types';
1717

1818
export class HTTP2Strategy implements HTTPRequestStrategy {
19-
public origin: string;
19+
#session: http2.ClientHttp2Session;
20+
2021
public closed: boolean = false;
21-
public session: http2.ClientHttp2Session;
22+
public origin: string;
2223

2324
constructor(baseURL: string) {
2425
this.origin = new URL(baseURL).origin;
25-
this.session = this._reviveSession();
26+
this.#session = this._reviveSession();
2627
}
2728

2829
public async request(info: InternalHTTPRequestInfo): Promise<GuaranteedAPIResponse> {
@@ -33,16 +34,16 @@ export class HTTP2Strategy implements HTTPRequestStrategy {
3334

3435
// Recreate session if session was closed except via an explicit `close()`
3536
// call. This happens when nginx sends a GOAWAY packet after 1000 requests.
36-
if (this.session.closed) {
37-
this.session = this._reviveSession();
37+
if (this.#session.closed) {
38+
this.#session = this._reviveSession();
3839
}
3940

4041
const timer = setTimeout(() => reject(info.timeoutError()), info.timeout);
4142

4243
const path = info.url.replace(this.origin, '');
4344
const params = info.params ? `?${new URLSearchParams(info.params).toString()}` : '';
4445

45-
const req = this.session.request({
46+
const req = this.#session.request({
4647
':path': path + params,
4748
':method': info.method,
4849
token: info.token,
@@ -88,13 +89,13 @@ export class HTTP2Strategy implements HTTPRequestStrategy {
8889
}
8990

9091
public close() {
91-
this.session.close();
92+
this.#session.close();
9293
this.closed = true;
9394
}
9495

9596
private _reviveSession() {
96-
if (this.session && !this.session.closed) {
97-
return this.session;
97+
if (this.#session && !this.#session.closed) {
98+
return this.#session;
9899
}
99100

100101
const session = http2.connect(this.origin);

src/client/data-api-client.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,31 +40,29 @@ export class DataApiClient {
4040
constructor(token: string, options?: RootClientOptions) {
4141
this.#options = {
4242
...options,
43-
dataApiOptions: {
43+
dbOptions: {
4444
token: token,
45-
...options?.dataApiOptions,
45+
...options?.dbOptions,
4646
},
47-
devopsOptions: {
47+
adminOptions: {
4848
adminToken: token,
49-
...options?.devopsOptions,
49+
...options?.adminOptions,
5050
},
5151
};
5252
}
5353

5454
/**
5555
* Spawns a new {@link Db} instance using a direct endpoint and given options.
5656
*
57+
* **NB. This method does not validate the existence of the database—it simply creates a reference.**
58+
*
5759
* This endpoint should include the protocol and the hostname, but not the path. It's typically in the form of
5860
* `https://<db_id>-<region>.apps.astra.datastax.com`, but it can be used with DSE or any other Data-API-compatible
5961
* endpoint.
6062
*
6163
* The given options will override any default options set when creating the {@link DataApiClient} through
6264
* a deep merge (i.e. unset properties in the options object will just default to the default options).
6365
*
64-
* Note that this does not perform any IO or validation on if the endpoint is valid or not. It's up to the user to
65-
* ensure that the endpoint is correct. If you want to create an actual database, see {@link AstraAdmin.createDatabase}
66-
* instead.
67-
*
6866
* @example
6967
* ```typescript
7068
* const db1 = client.db('https://<db_id>-<region>.apps.astra.datastax.com');
@@ -75,6 +73,11 @@ export class DataApiClient {
7573
* });
7674
* ```
7775
*
76+
* @remarks
77+
* Note that this does not perform any IO or validation on if the endpoint is valid or not. It's up to the user to
78+
* ensure that the endpoint is correct. If you want to create an actual database, see {@link AstraAdmin.createDatabase}
79+
* instead.
80+
*
7881
* @param endpoint - The direct endpoint to use.
7982
* @param options - Any options to override the default options set when creating the {@link DataApiClient}.
8083
*
@@ -85,16 +88,14 @@ export class DataApiClient {
8588
/**
8689
* Spawns a new {@link Db} instance using a direct endpoint and given options.
8790
*
91+
* **NB. This method does not validate the existence of the database—it simply creates a reference.**
92+
*
8893
* This overload is purely for user convenience, but it **only supports using Astra as the underlying database**. For
8994
* DSE or any other Data-API-compatible endpoint, use the other overload instead.
9095
*
9196
* The given options will override any default options set when creating the {@link DataApiClient} through
9297
* a deep merge (i.e. unset properties in the options object will just default to the default options).
9398
*
94-
* Note that this does not perform any IO or validation on if the endpoint is valid or not. It's up to the user to
95-
* ensure that the endpoint is correct. If you want to create an actual database, see {@link AstraAdmin.createDatabase}
96-
* instead.
97-
*
9899
* @example
99100
* ```typescript
100101
* const db1 = client.db('a6a1d8d6-31bc-4af8-be57-377566f345bf', 'us-east1');
@@ -105,6 +106,11 @@ export class DataApiClient {
105106
* });
106107
* ```
107108
*
109+
* @remarks
110+
* Note that this does not perform any IO or validation on if the endpoint is valid or not. It's up to the user to
111+
* ensure that the endpoint is correct. If you want to create an actual database, see {@link AstraAdmin.createDatabase}
112+
* instead.
113+
*
108114
* @param id - The database ID to use.
109115
* @param region - The region to use.
110116
* @param options - Any options to override the default options set when creating the {@link DataApiClient}.

src/client/types.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ export interface RootClientOptions {
4444
/**
4545
* The default options when spawning a {@link Db} instance.
4646
*/
47-
dataApiOptions?: DbSpawnOptions,
47+
dbOptions?: DbSpawnOptions,
4848
/**
4949
* The default options when spawning an {@link AstraAdmin} instance.
5050
*/
51-
devopsOptions?: AdminSpawnOptions,
51+
adminOptions?: AdminSpawnOptions,
5252
}
5353

5454
/**
@@ -189,6 +189,6 @@ export interface AdminSpawnOptions {
189189
export interface RootClientOptsWithToken {
190190
logLevel?: string,
191191
caller?: Caller | Caller[],
192-
dataApiOptions: DbSpawnOptions & { token: string },
193-
devopsOptions: AdminSpawnOptions & { adminToken: string },
192+
dbOptions: DbSpawnOptions & { token: string },
193+
adminOptions: AdminSpawnOptions & { adminToken: string },
194194
}

src/data-api/db.ts

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { RunCommandOptions } from '@/src/data-api/types/collections/command';
3434
/**
3535
* @internal
3636
*/
37-
type DbOptions = RootClientOptsWithToken & { dataApiOptions: { token: string } };
37+
type DbOptions = RootClientOptsWithToken & { dbOptions: { token: string } };
3838

3939
/**
4040
* Represents an interface to some Astra database instance. This is the entrypoint for database-level DML, such as
@@ -68,7 +68,7 @@ type DbOptions = RootClientOptsWithToken & { dataApiOptions: { token: string } }
6868
* @see DataApiClient.db
6969
* @see AstraAdmin.db
7070
*/
71-
export class Db {
71+
export class Db implements Disposable {
7272
readonly #defaultOpts!: RootClientOptsWithToken;
7373

7474
private readonly _httpClient!: DataApiHttpClient;
@@ -85,7 +85,7 @@ export class Db {
8585
*
8686
* // Overrides the default namespace for all future db spawns
8787
* const client2 = new DataApiClient('AstraCS:...', {
88-
*   dataApiOptions: { namespace: 'my_namespace' }
88+
*   dbOptions: { namespace: 'my_namespace' }
8989
* });
9090
*
9191
* // Created with 'default_keyspace' as the default namespace
@@ -113,7 +113,7 @@ export class Db {
113113
* @internal
114114
*/
115115
constructor(endpoint: string, options: DbOptions) {
116-
const dbOpts = options.dataApiOptions ?? {};
116+
const dbOpts = options.dbOptions ?? {};
117117

118118
Object.defineProperty(this, 'namespace', {
119119
value: dbOpts.namespace ?? DEFAULT_NAMESPACE,
@@ -150,7 +150,7 @@ export class Db {
150150
/**
151151
* The ID of the database, if it's an Astra database. If it's a non-Astra database, this will throw an error.
152152
*
153-
* @throws An error if the database is not an Astra database.
153+
* @throws Error - if the database is not an Astra database.
154154
*/
155155
get id(): string {
156156
if (!this._id) {
@@ -181,7 +181,7 @@ export class Db {
181181
*
182182
* @returns A new {@link AstraDbAdmin} instance for this database instance.
183183
*
184-
* @throws An error if the database is not an Astra database.
184+
* @throws Error - if the database is not an Astra database.
185185
*/
186186
public admin(options?: AdminSpawnOptions): AstraDbAdmin {
187187
if (!this._id) {
@@ -208,7 +208,7 @@ export class Db {
208208
*
209209
* @returns A promise that resolves to the database information.
210210
*
211-
* @throws An error if the database is not an Astra database.
211+
* @throws Error - if the database is not an Astra database.
212212
*/
213213
public async info(): Promise<DatabaseInfo> {
214214
return await this.admin().info().then(i => i.info);
@@ -441,6 +441,49 @@ export class Db {
441441
public async command(command: Record<string, any>, options?: RunCommandOptions): Promise<RawDataApiResponse> {
442442
return await this._httpClient.executeCommand(command, options);
443443
}
444+
445+
/**
446+
* @returns whether the client is using HTTP/2 for requests.
447+
*/
448+
public httpStrategy(): 'http1' | 'http2' {
449+
return this._httpClient.isUsingHttp2()
450+
? 'http2'
451+
: 'http1';
452+
}
453+
454+
/**
455+
* Returns if the underlying HTTP/2 session was explicitly closed by the {@link close} method (or through ERM with
456+
* the `using` clause).
457+
*
458+
* If the client's using HTTP/1, this method will return `undefined`.
459+
*
460+
* @returns whether the client was explicitly closed if using HTTP/2.
461+
*/
462+
public isClosed(): boolean | undefined {
463+
return this._httpClient.isClosed();
464+
}
465+
466+
/**
467+
* Closes the underlying HTTP/2 session, if the client is using HTTP/2. Closing the session will prevent any further
468+
* requests from being made from this Db instance.
469+
*
470+
* This method is idempotent. If the client is using HTTP/1, this method will do nothing.
471+
*/
472+
public close() {
473+
this._httpClient.close();
474+
}
475+
476+
/**
477+
* Closes the underlying HTTP/2 session, if the client is using HTTP/2. Closing the session will prevent any further
478+
* requests from being made from this Db instance.
479+
*
480+
* This method is idempotent. If the client is using HTTP/1, this method will do nothing.
481+
*
482+
* Meant for usage with the `using` clause in ERM (Explicit Resource Management).
483+
*/
484+
[Symbol.dispose]() {
485+
this.close();
486+
}
444487
}
445488

446489
/**
@@ -457,8 +500,8 @@ export function mkDb(rootOpts: RootClientOptsWithToken, endpointOrId: string, re
457500

458501
return new Db(endpoint, {
459502
...rootOpts,
460-
dataApiOptions: {
461-
...rootOpts?.dataApiOptions,
503+
dbOptions: {
504+
...rootOpts?.dbOptions,
462505
...options,
463506
},
464507
});

0 commit comments

Comments
 (0)