Skip to content

Commit

Permalink
added rpush and rpop commands in node.
Browse files Browse the repository at this point in the history
  • Loading branch information
Adan committed Oct 24, 2023
1 parent 8785360 commit 4e84cc8
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 0 deletions.
34 changes: 34 additions & 0 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import {
createLRange,
createMGet,
createMSet,
createRPop,
createRPush,
createSet,
} from "./Commands";
import {
Expand Down Expand Up @@ -572,6 +574,38 @@ export class BaseClient {
return this.createWritePromise(createLRange(key, start, end));
}

/** Inserts all the specified values at the tail of the list stored at `key`.
* `elements` are inserted one after the other to the tail of the list, from the leftmost element to the rightmost element.
* If `key` does not exist, it is created as empty list before performing the push operations.
* See https://redis.io/commands/rpush/ for details.
*
* @param key - The key of the list.
* @param elements - The elements to insert at the tail of the list stored at `key`.
* @returns the length of the list after the push operations.
* If `key` holds a value that is not a list, an error is raised.
*/
public rpush(key: string, elements: string[]): Promise<number> {
return this.createWritePromise(createRPush(key, elements));
}

/** Removes and returns the last elements of the list stored at `key`.
* By default, the command pops a single element from the end of the list.
* When `count` is provided, the command pops up to `count` elements, depending on the list's length.
* See https://redis.io/commands/rpop/ for details.
*
* @param key - The key of the list.
* @param count - The count of the elements to pop from the list.
* @returns The value of the last element if `count` is not provided. If `count` is provided, list of popped elements will be returned depending on the list's length.
* If `key` does not exist null will be returned.
* If `key` holds a value that is not a list, an error is raised.
*/
public rpop(
key: string,
count?: number
): Promise<string | string[] | null> {
return this.createWritePromise(createRPop(key, count));
}

private readonly MAP_READ_FROM_REPLICA_STRATEGY: Record<
ReadFromReplicaStrategy,
connection_request.ReadFromReplicaStrategy
Expand Down
12 changes: 12 additions & 0 deletions node/src/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,18 @@ export function createLRange(
]);
}

export function createRPush(
key: string,
elements: string[]
): redis_request.Command {
return createCommand(RequestType.RPush, [key].concat(elements));
}

export function createRPop(key: string, count?: number): redis_request.Command {
const args: string[] = count == undefined ? [key] : [key, count.toString()];
return createCommand(RequestType.RPop, args);
}

export function createCustomCommand(commandName: string, args: string[]) {
return createCommand(RequestType.CustomCommand, [commandName, ...args]);
}
Expand Down
33 changes: 33 additions & 0 deletions node/src/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import {
createMGet,
createMSet,
createPing,
createRPop,
createRPush,
createSelect,
createSet,
} from "./Commands";
Expand Down Expand Up @@ -432,6 +434,37 @@ export class BaseTransaction {
this.commands.push(createLRange(key, start, end));
}

/** Inserts all the specified values at the tail of the list stored at `key`.
* `elements` are inserted one after the other to the tail of the list, from the leftmost element to the rightmost element.
* If `key` does not exist, it is created as empty list before performing the push operations.
* See https://redis.io/commands/rpush/ for details.
*
* @param key - The key of the list.
* @param elements - The elements to insert at the tail of the list stored at `key`.
*
* Command Response - the length of the list after the push operations.
* If `key` holds a value that is not a list, an error is raised.
*/
public rpush(key: string, elements: string[]) {
this.commands.push(createRPush(key, elements));
}

/** Removes and returns the last elements of the list stored at `key`.
* By default, the command pops a single element from the end of the list.
* When `count` is provided, the command pops up to `count` elements, depending on the list's length.
* See https://redis.io/commands/rpop/ for details.
*
* @param key - The key of the list.
* @param count - The count of the elements to pop from the list.
*
* Command Response - The value of the last element if `count` is not provided. If `count` is provided, list of popped elements will be returned depending on the list's length.
* If `key` does not exist null will be returned.
* If `key` holds a value that is not a list, an error is raised.
*/
public rpop(key: string, count?: number) {
this.commands.push(createRPop(key, count));
}

/** Executes a single command, without checking inputs. Every part of the command, including subcommands,
* should be added as a separate value in args.
*
Expand Down
44 changes: 44 additions & 0 deletions node/tests/SharedTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ type BaseClient = {
lpush: (key: string, elements: string[]) => Promise<number>;
lpop: (key: string, count?: number) => Promise<string | string[] | null>;
lrange: (key: string, start: number, end: number) => Promise<string[]>;
rpush: (key: string, elements: string[]) => Promise<number>;
rpop: (key: string, count?: number) => Promise<string | string[] | null>;
customCommand: (commandName: string, args: string[]) => Promise<ReturnType>;
};

Expand Down Expand Up @@ -753,6 +755,48 @@ export function runBaseTests<Context>(config: {
},
config.timeout
);

it(
"rpush and rpop with existing and non existing key",
async () => {
await runTest(async (client: BaseClient) => {
const key = uuidv4();
const valueList = ["value1", "value2", "value3", "value4"];
expect(await client.rpush(key, valueList)).toEqual(4);
expect(await client.rpop(key)).toEqual("value4");
expect(await client.rpop(key, 2)).toEqual(["value3", "value2"]);
expect(await client.rpop("nonExistingKey")).toEqual(null);
});
},
config.timeout
);

it(
"rpush and rpop with key that holds a value that is not a list",
async () => {
await runTest(async (client: BaseClient) => {
const key = uuidv4();
expect(await client.set(key, "foo")).toEqual("OK");

try {
expect(await client.rpush(key, ["bar"])).toThrow();
} catch (e) {
expect((e as Error).message).toMatch(
"Operation against a key holding the wrong kind of value"
);
}

try {
expect(await client.rpop(key)).toThrow();
} catch (e) {
expect((e as Error).message).toMatch(
"Operation against a key holding the wrong kind of value"
);
}
});
},
config.timeout
);
}

export function runCommonTests<Context>(config: {
Expand Down
5 changes: 5 additions & 0 deletions node/tests/TestUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export function transactionTest(
const key3 = "{key}" + uuidv4();
const key4 = "{key}" + uuidv4();
const key5 = "{key}" + uuidv4();
const key6 = "{key}" + uuidv4();
const field = uuidv4();
const value = uuidv4();
baseTransaction.set(key1, "bar");
Expand All @@ -73,6 +74,8 @@ export function transactionTest(
baseTransaction.hexists(key4, field);
baseTransaction.lpush(key5, [field + "1", field + "2"]);
baseTransaction.lpop(key5);
baseTransaction.rpush(key6, [field + "1", field + "2"]);
baseTransaction.rpop(key6);
return [
"OK",
null,
Expand All @@ -88,5 +91,7 @@ export function transactionTest(
0,
2,
field + "2",
2,
field + "2",
];
}

0 comments on commit 4e84cc8

Please sign in to comment.