Skip to content

Commit

Permalink
Node: Add command GETEX (valkey-io#2107)
Browse files Browse the repository at this point in the history
* Node: Add command GETEX

Signed-off-by: TJ Zhang <[email protected]>
  • Loading branch information
tjzhang-BQ authored Aug 14, 2024
1 parent eb92d07 commit 27342ab
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 61 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
* Node: Added XGROUP CREATE & XGROUP DESTROY commands ([#2084](https://github.com/valkey-io/valkey-glide/pull/2084))
* Node: Added BZPOPMAX & BZPOPMIN command ([#2077]((https://github.com/valkey-io/valkey-glide/pull/2077))
* Node: Added XGROUP CREATECONSUMER & XGROUP DELCONSUMER commands ([#2088](https://github.com/valkey-io/valkey-glide/pull/2088))
* Node: Added GETEX command ([#2107]((https://github.com/valkey-io/valkey-glide/pull/2107))

#### Breaking Changes
* Node: (Refactor) Convert classes to types ([#2005](https://github.com/valkey-io/valkey-glide/pull/2005))
Expand Down
2 changes: 2 additions & 0 deletions node/npm/glide/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ function initialize() {
FunctionStatsResponse,
SlotIdTypes,
SlotKeyTypes,
TimeUnit,
RouteByAddress,
Routes,
SingleNodeRoute,
Expand Down Expand Up @@ -203,6 +204,7 @@ function initialize() {
SlotIdTypes,
SlotKeyTypes,
StreamEntries,
TimeUnit,
ReturnTypeXinfoStream,
RouteByAddress,
Routes,
Expand Down
30 changes: 29 additions & 1 deletion node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import {
createGet,
createGetBit,
createGetDel,
createGetEx,
createGetRange,
createHDel,
createHExists,
Expand Down Expand Up @@ -204,6 +205,7 @@ import {
createZRevRankWithScore,
createZScan,
createZScore,
TimeUnit,
} from "./Commands";
import {
ClosingError,
Expand Down Expand Up @@ -933,6 +935,32 @@ export class BaseClient {
return this.createWritePromise(createGet(key), { decoder: decoder });
}

/**
* Get the value of `key` and optionally set its expiration. `GETEX` is similar to {@link get}.
*
* See https://valkey.io/commands/getex for more details.
*
* @param key - The key to retrieve from the database.
* @param options - (Optional) Set expiriation to the given key.
* "persist" will retain the time to live associated with the key. Equivalent to `PERSIST` in the VALKEY API.
* Otherwise, a {@link TimeUnit} and duration of the expire time should be specified.
* @returns If `key` exists, returns the value of `key` as a `string`. Otherwise, return `null`.
*
* since - Valkey 6.2.0 and above.
*
* @example
* ```typescript
* const result = await client.getex("key", {expiry: { type: TimeUnit.Seconds, count: 5 }});
* console.log(result); // Output: 'value'
* ```
*/
public async getex(
key: string,
options?: "persist" | { type: TimeUnit; duration: number },
): Promise<string | null> {
return this.createWritePromise(createGetEx(key, options));
}

/**
* Gets a string value associated with the given `key`and deletes the key.
*
Expand Down Expand Up @@ -1005,7 +1033,7 @@ export class BaseClient {
* console.log(result); // Output: 'OK'
*
* // Example usage of set method with conditional options and expiration
* const result2 = await client.set("key", "new_value", {conditionalSet: "onlyIfExists", expiry: { type: "seconds", count: 5 }});
* const result2 = await client.set("key", "new_value", {conditionalSet: "onlyIfExists", expiry: { type: TimeUnit.Seconds, count: 5 }});
* console.log(result2); // Output: 'OK' - Set "new_value" to "key" only if "key" already exists, and set the key expiration to 5 seconds.
*
* // Example usage of set method with conditional options and returning old value
Expand Down
112 changes: 71 additions & 41 deletions node/src/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,26 +142,7 @@ export type SetOptions = {
*/
| "keepExisting"
| {
type: /**
* Set the specified expire time, in seconds. Equivalent to
* `EX` in the Redis API.
*/
| "seconds"
/**
* Set the specified expire time, in milliseconds. Equivalent
* to `PX` in the Redis API.
*/
| "milliseconds"
/**
* Set the specified Unix time at which the key will expire,
* in seconds. Equivalent to `EXAT` in the Redis API.
*/
| "unixSeconds"
/**
* Set the specified Unix time at which the key will expire,
* in milliseconds. Equivalent to `PXAT` in the Redis API.
*/
| "unixMilliseconds";
type: TimeUnit;
count: number;
};
};
Expand All @@ -187,28 +168,23 @@ export function createSet(
args.push("GET");
}

if (
options.expiry &&
options.expiry !== "keepExisting" &&
!Number.isInteger(options.expiry.count)
) {
throw new Error(
`Received expiry '${JSON.stringify(
options.expiry,
)}'. Count must be an integer`,
);
}
if (options.expiry) {
if (
options.expiry !== "keepExisting" &&
!Number.isInteger(options.expiry.count)
) {
throw new Error(
`Received expiry '${JSON.stringify(
options.expiry,
)}'. Count must be an integer`,
);
}

if (options.expiry === "keepExisting") {
args.push("KEEPTTL");
} else if (options.expiry?.type === "seconds") {
args.push("EX", options.expiry.count.toString());
} else if (options.expiry?.type === "milliseconds") {
args.push("PX", options.expiry.count.toString());
} else if (options.expiry?.type === "unixSeconds") {
args.push("EXAT", options.expiry.count.toString());
} else if (options.expiry?.type === "unixMilliseconds") {
args.push("PXAT", options.expiry.count.toString());
if (options.expiry === "keepExisting") {
args.push("KEEPTTL");
} else {
args.push(options.expiry.type, options.expiry.count.toString());
}
}
}

Expand Down Expand Up @@ -3640,3 +3616,57 @@ export function createBZPopMin(
): command_request.Command {
return createCommand(RequestType.BZPopMin, [...keys, timeout.toString()]);
}

/**
* Time unit representation which is used in optional arguments for {@link BaseClient.getex|getex} and {@link BaseClient.set|set} command.
*/
export enum TimeUnit {
/**
* Set the specified expire time, in seconds. Equivalent to
* `EX` in the VALKEY API.
*/
Seconds = "EX",
/**
* Set the specified expire time, in milliseconds. Equivalent
* to `PX` in the VALKEY API.
*/
Milliseconds = "PX",
/**
* Set the specified Unix time at which the key will expire,
* in seconds. Equivalent to `EXAT` in the VALKEY API.
*/
UnixSeconds = "EXAT",
/**
* Set the specified Unix time at which the key will expire,
* in milliseconds. Equivalent to `PXAT` in the VALKEY API.
*/
UnixMilliseconds = "PXAT",
}

/**
* @internal
*/
export function createGetEx(
key: string,
options?: "persist" | { type: TimeUnit; duration: number },
): command_request.Command {
const args = [key];

if (options) {
if (options !== "persist" && !Number.isInteger(options.duration)) {
throw new Error(
`Received expiry '${JSON.stringify(
options.duration,
)}'. Count must be an integer`,
);
}

if (options === "persist") {
args.push("PERSIST");
} else {
args.push(options.type, options.duration.toString());
}
}

return createCommand(RequestType.GetEx, args);
}
23 changes: 23 additions & 0 deletions node/src/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ import {
createGet,
createGetBit,
createGetDel,
createGetEx,
createGetRange,
createHDel,
createHExists,
Expand Down Expand Up @@ -240,6 +241,7 @@ import {
createZRevRankWithScore,
createZScan,
createZScore,
TimeUnit,
} from "./Commands";
import { command_request } from "./ProtobufMessage";

Expand Down Expand Up @@ -293,6 +295,7 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
}

/** Get the value associated with the given key, or null if no such value exists.
*
* See https://valkey.io/commands/get/ for details.
*
* @param key - The key to retrieve from the database.
Expand All @@ -303,6 +306,26 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
return this.addAndReturn(createGet(key));
}

/**
* Get the value of `key` and optionally set its expiration. `GETEX` is similar to {@link get}.
* See https://valkey.io/commands/getex for more details.
*
* @param key - The key to retrieve from the database.
* @param options - (Optional) set expiriation to the given key.
* "persist" will retain the time to live associated with the key. Equivalent to `PERSIST` in the VALKEY API.
* Otherwise, a {@link TimeUnit} and duration of the expire time should be specified.
*
* since - Valkey 6.2.0 and above.
*
* Command Response - If `key` exists, returns the value of `key` as a `string`. Otherwise, return `null`.
*/
public getex(
key: string,
options?: "persist" | { type: TimeUnit; duration: number },
): T {
return this.addAndReturn(createGetEx(key, options));
}

/**
* Gets a string value associated with the given `key`and deletes the key.
*
Expand Down
3 changes: 2 additions & 1 deletion node/tests/GlideClientInternals.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
RequestError,
ReturnType,
SlotKeyTypes,
TimeUnit,
} from "..";
import {
command_request,
Expand Down Expand Up @@ -545,7 +546,7 @@ describe("SocketConnectionInternals", () => {
const request1 = connection.set("foo", "bar", {
conditionalSet: "onlyIfExists",
returnOldValue: true,
expiry: { type: "seconds", count: 10 },
expiry: { type: TimeUnit.Seconds, count: 10 },
});

expect(await request1).toMatch("OK");
Expand Down
Loading

0 comments on commit 27342ab

Please sign in to comment.