Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,19 @@ nav:
- Standalone Batch: python/standalone_batch.md
- Cluster Batch: python/cluster_batch.md
- Standalone Transaction (Deprecated): python/standalone_transaction.md
- Cluster Transaction (Deprecated)): python/cluster_transaction.md
- Cluster Transaction (Deprecated): python/cluster_transaction.md
- Exceptions: python/exceptions.md
- Logger: python/logger.md
- TypeScript:
- Client and Commands:
- Standalone: node/GlideClient/classes/GlideClient.md
- Cluster: node/GlideClusterClient/classes/GlideClusterClient.md
- Base: node/BaseClient/classes/BaseClient.md
- Transaction: node/Transaction/classes/Transaction.md
- Base Batch: node/Batch/classes/BaseBatch.md
- Standalone Batch: node/Batch/classes/Batch.md
- Cluster Batch: node/Batch/classes/ClusterBatch.md
- Transaction (Deprecated): node/Batch/classes/Transaction.md
- Cluster Transaction (Deprecated): node/Batch/classes/ClusterTransaction.md
- config: node/BaseClient/interfaces/BaseClientConfiguration.md
- Modules:
- JSON: node/server-modules/GlideJson/classes/GlideJson.md
Expand Down
2 changes: 1 addition & 1 deletion node/DEVELOPER.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ npm test-minimum
To execute a specific test, use the [`testNamePattern`](https://jestjs.io/docs/cli#--testnamepatternregex) option with `test-dbg` script. For example:

```bash
npm run test-dbg -- --testNamePattern="transaction"
npm run test-dbg -- --testNamePattern="batch"
```

IT suite starts the server for testing - standalone and cluster installation using `cluster_manager` script.
Expand Down
2 changes: 1 addition & 1 deletion node/docs/build-docs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash -e
npm install --save-dev typedoc
typedoc src/BaseClient.ts \
src/Transaction.ts \
src/Batch.ts \
src/GlideClient.ts \
src/GlideClusterClient.ts \
src/Errors.ts \
Expand Down
4 changes: 2 additions & 2 deletions node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

export { ClusterScanCursor, Script } from "glide-rs";
export * from "./src/BaseClient";
export * from "./src/Batch";
export * from "./src/Commands";
export * from "./src/Errors";
export * from "./src/GlideClient";
export * from "./src/GlideClusterClient";
export * from "./src/Logger";
export * from "./src/server-modules/GlideJson";
export * from "./src/server-modules/GlideFt";
export * from "./src/server-modules/GlideFtOptions";
export * from "./src/Transaction";
export * from "./src/server-modules/GlideJson";
10 changes: 10 additions & 0 deletions node/npm/glide/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ function initialize() {
ConnectionError,
ClusterTransaction,
Transaction,
Batch,
ClusterBatch,
PubSubMsg,
ScoreFilter,
SignedEncoding,
Expand Down Expand Up @@ -193,6 +195,9 @@ function initialize() {
UniversalReturnTypeJson,
Score,
ElementAndScore,
BatchOptions,
ClusterBatchOptions,
ClusterBatchRetryStrategy,
} = nativeBinding;

module.exports = {
Expand Down Expand Up @@ -319,6 +324,8 @@ function initialize() {
ConnectionError,
ClusterTransaction,
Transaction,
Batch,
ClusterBatch,
PubSubMsg,
ScoreFilter,
SignedEncoding,
Expand All @@ -344,6 +351,9 @@ function initialize() {
UniversalReturnTypeJson,
Score,
ElementAndScore,
BatchOptions,
ClusterBatchOptions,
ClusterBatchRetryStrategy,
};

globalObject = Object.assign(global, nativeBinding);
Expand Down
3 changes: 3 additions & 0 deletions node/npm/glide/tests/ExportedSymbols.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ const skippedListForExports: string[] = [
"ClusterScanOptions",
"GlideMultiJson",
"BaseClient",
"BaseBatch",
"BaseBatchOptions",
"convertFieldsAndValuesToHashDataType",
"parseInfoResponse",
"command_request",
"connection_request",
"response",
"WritePromiseOptions",
];

const glideRsKeyWords: string[] = [
Expand Down
13 changes: 8 additions & 5 deletions node/rust-client/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0

use glide_core::errors::error_message;
use glide_core::Telemetry;
use redis::GlideConnectionOptions;

Expand Down Expand Up @@ -276,11 +277,13 @@ fn resp_value_to_js(val: Value, js_env: Env, string_decoder: bool) -> Result<JsU
obj.set_named_property("values", js_array_view)?;
Ok(obj.into_unknown())
}
Value::ServerError(_) => Err(Error::new(
// TODO: add ServerError support
Status::GenericFailure,
"ServerError is not supported",
)),
Value::ServerError(error) => {
let err_msg = error_message(&error.into());
let err = Error::new(Status::Ok, err_msg);
let mut js_error = js_env.create_error(err)?;
js_error.set_named_property("name", "RequestError")?;
Ok(js_error.into_unknown())
}
}
}

Expand Down
154 changes: 124 additions & 30 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Buffer, BufferWriter, Long, Reader, Writer } from "protobufjs";
import {
AggregationType,
BaseScanOptions,
BatchOptions,
BitFieldGet,
BitFieldIncrBy, // eslint-disable-line @typescript-eslint/no-unused-vars
BitFieldOverflow, // eslint-disable-line @typescript-eslint/no-unused-vars
Expand All @@ -25,6 +26,7 @@ import {
BitOffsetOptions,
BitwiseOperation,
Boundary,
ClusterBatchOptions,
CoordOrigin, // eslint-disable-line @typescript-eslint/no-unused-vars
ExpireOptions,
GeoAddOptions,
Expand Down Expand Up @@ -281,6 +283,7 @@ export type GlideReturnType =
| ReturnTypeRecord
| ReturnTypeMap
| ReturnTypeAttribute
| RequestError
| GlideReturnType[];

/**
Expand Down Expand Up @@ -753,10 +756,20 @@ export interface PubSubMsg {

/**
* @internal
* A type to combine RouterOption and DecoderOption to be used for creating write promises for the command.
* See - {@link DecoderOption} and {@link RouteOption}
* Options used when creating a write promise for a command.
*
* This type combines {@link RouteOption} and {@link DecoderOption} with optional support for
* {@link ClusterBatchOptions} or {@link BatchOptions}.
*
* @see {@link DecoderOption}
* @see {@link RouteOption}
* @see {@link ClusterBatchOptions}
* @see {@link BatchOptions}
*/
export type WritePromiseOptions = RouteOption & DecoderOption;
type BaseOptions = RouteOption & DecoderOption;
export type WritePromiseOptions =
| BaseOptions
| (BaseOptions & (ClusterBatchOptions | BatchOptions));

/**
* Base client interface for GLIDE
Expand Down Expand Up @@ -1065,22 +1078,68 @@ export class BaseClient {

/**
* @internal
*
* Creates a promise that resolves or rejects based on the result of a command request.
*
* @template T - The type of the result expected from the promise.
* @param command - A single command or an array of commands to be executed, array of commands represents a batch and not a single command.
* @param options - Optional settings for the write operation, including route, batch options and decoder.
* @param isAtomic - Indicates whether the operation should be executed atomically (AKA as a Transaction, in the case of a batch). Defaults to `false`.
* @param raiseOnError - Determines whether to raise an error if any of the commands fails, in the case of a Batch and not a single command. Defaults to `false`.
* @returns A promise that resolves with the result of the command(s) or rejects with an error.
*/
protected createWritePromise<T>(
command: command_request.Command | command_request.Command[],
options: WritePromiseOptions = {},
isAtomic = false,
raiseOnError = false,
): Promise<T> {
this.ensureClientIsOpen();

const route = this.toProtobufRoute(options?.route);
return new Promise((resolve, reject) => {
const callbackIndex = this.getCallbackIndex();
const callbackIndex = this.getCallbackIndex();
const basePromise = new Promise<T>((resolve, reject) => {
this.promiseCallbackFunctions[callbackIndex] = [
resolve,
reject,
options?.decoder,
];
this.writeOrBufferCommandRequest(callbackIndex, command, route);

this.writeOrBufferCommandRequest(
callbackIndex,
command,
route,
isAtomic,
raiseOnError,
options as ClusterBatchOptions,
);
});

if (!Array.isArray(command)) {
return basePromise;
}

return basePromise.then((result: T) => {
if (Array.isArray(result)) {
const loopLen = result.length;

for (let i = 0; i < loopLen; i++) {
const item = result[i];

// Check if the item is an instance of napi Error
// Can be checked by checking if the constructor name is "Error"
// and if there is a name property with the value "RequestError" (that we added in the Rust code)
if (
item?.constructor?.name === "Error" &&
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
(item as any).name === "RequestError"
) {
Object.setPrototypeOf(item, RequestError.prototype);
}
}
}

return result;
});
}

Expand Down Expand Up @@ -1138,31 +1197,64 @@ export class BaseClient {
});
}

/**
* @internal
*
* @param callbackIdx - The requests callback index.
* @param command - A single command or an array of commands to be executed, array of commands represents a batch and not a single command.
* @param route - Optional routing information for the command.
* @param isAtomic - Indicates whether the operation should be executed atomically (AKA as a Transaction, in the case of a batch). Defaults to `false`.
* @param raiseOnError - Determines whether to raise an error if any of the commands fails, in the case of a Batch and not a single command. Defaults to `false`.
* @param options - Optional settings for batch requests.
*/
protected writeOrBufferCommandRequest(
callbackIdx: number,
command: command_request.Command | command_request.Command[],
route?: command_request.Routes,
isAtomic = false,
raiseOnError = false,
options: ClusterBatchOptions | BatchOptions = {},
) {
const message = Array.isArray(command)
? command_request.CommandRequest.create({
callbackIdx,
batch: command_request.Batch.create({
isAtomic: true,
commands: command,
// TODO: add support for timeout, raiseOnError and retryStrategy
}),
route,
})
: command_request.CommandRequest.create({
callbackIdx,
singleCommand: command,
route,
});
if (isAtomic && "retryStrategy" in options) {
throw new RequestError(
"Retry strategy is not supported for atomic batches.",
);
}

const isBatch = Array.isArray(command);
let batch: command_request.Batch | undefined;

if (isBatch) {
let retryServerError: boolean | undefined;
let retryConnectionError: boolean | undefined;

if ("retryStrategy" in options) {
retryServerError = options.retryStrategy?.retryServerError;
retryConnectionError =
options.retryStrategy?.retryConnectionError;
}

batch = command_request.Batch.create({
isAtomic,
commands: command,
raiseOnError,
timeout: options.timeout,
retryServerError,
retryConnectionError,
});
}

const message = command_request.CommandRequest.create({
callbackIdx,
singleCommand: isBatch ? undefined : command,
batch,
route,
});

this.writeOrBufferRequest(
message,
(message: command_request.CommandRequest, writer: Writer) => {
command_request.CommandRequest.encodeDelimited(message, writer);
(msg: command_request.CommandRequest, writer: Writer) => {
command_request.CommandRequest.encodeDelimited(msg, writer);
},
);
}
Expand All @@ -1180,7 +1272,7 @@ export class BaseClient {
this.writeBufferedRequestsToSocket();
}

// Define a common function to process the result of a transaction with set commands
// Define a common function to process the result of a batch with set commands
/**
* @internal
*/
Expand All @@ -1192,10 +1284,12 @@ export class BaseClient {
return null;
}

for (const index of setCommandsIndexes) {
result[index] = new Set<GlideReturnType>(
result[index] as GlideReturnType[],
);
for (let i = 0, len = setCommandsIndexes.length; i < len; i++) {
if (Array.isArray(result[setCommandsIndexes[i]])) {
result[setCommandsIndexes[i]] = new Set<GlideReturnType>(
result[setCommandsIndexes[i]] as GlideReturnType[],
);
}
}

return result;
Expand Down Expand Up @@ -7287,14 +7381,14 @@ export class BaseClient {
* ```typescript
* const response = await client.watch(["sampleKey"]);
* console.log(response); // Output: "OK"
* const transaction = new Transaction().set("SampleKey", "foobar");
* const transaction = new Batch(true).set("SampleKey", "foobar");
* const result = await client.exec(transaction);
* console.log(result); // Output: "OK" - Executes successfully and keys are unwatched.
* ```
* ```typescript
* const response = await client.watch(["sampleKey"]);
* console.log(response); // Output: "OK"
* const transaction = new Transaction().set("SampleKey", "foobar");
* const transaction = new Batch(true).set("SampleKey", "foobar");
* await client.set("sampleKey", "hello world");
* const result = await client.exec(transaction);
* console.log(result); // Output: null - null is returned when the watched key is modified before transaction execution.
Expand Down
Loading
Loading