Skip to content

Commit b4df2b2

Browse files
authored
add support for all hash field expiration commands (#2787)
1 parent 72345fe commit b4df2b2

20 files changed

+555
-1
lines changed

packages/client/lib/cluster/commands.ts

+27
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,29 @@ import * as GETRANGE from '../commands/GETRANGE';
5353
import * as GETSET from '../commands/GETSET';
5454
import * as HDEL from '../commands/HDEL';
5555
import * as HEXISTS from '../commands/HEXISTS';
56+
import * as HEXPIRE from '../commands/HEXPIRE';
57+
import * as HEXPIREAT from '../commands/HEXPIREAT';
58+
import * as HEXPIRETIME from '../commands/HEXPIRETIME';
5659
import * as HGET from '../commands/HGET';
5760
import * as HGETALL from '../commands/HGETALL';
5861
import * as HINCRBY from '../commands/HINCRBY';
5962
import * as HINCRBYFLOAT from '../commands/HINCRBYFLOAT';
6063
import * as HKEYS from '../commands/HKEYS';
6164
import * as HLEN from '../commands/HLEN';
6265
import * as HMGET from '../commands/HMGET';
66+
import * as HPERSIST from '../commands/HPERSIST';
67+
import * as HPEXPIRE from '../commands/HPEXPIRE';
68+
import * as HPEXPIREAT from '../commands/HPEXPIREAT';
69+
import * as HPEXPIRETIME from '../commands/HPEXPIRETIME';
70+
import * as HPTTL from '../commands/HPTTL';
6371
import * as HRANDFIELD_COUNT_WITHVALUES from '../commands/HRANDFIELD_COUNT_WITHVALUES';
6472
import * as HRANDFIELD_COUNT from '../commands/HRANDFIELD_COUNT';
6573
import * as HRANDFIELD from '../commands/HRANDFIELD';
6674
import * as HSCAN from '../commands/HSCAN';
6775
import * as HSET from '../commands/HSET';
6876
import * as HSETNX from '../commands/HSETNX';
6977
import * as HSTRLEN from '../commands/HSTRLEN';
78+
import * as HTTL from '../commands/HTTL';
7079
import * as HVALS from '../commands/HVALS';
7180
import * as INCR from '../commands/INCR';
7281
import * as INCRBY from '../commands/INCRBY';
@@ -321,6 +330,12 @@ export default {
321330
hDel: HDEL,
322331
HEXISTS,
323332
hExists: HEXISTS,
333+
HEXPIRE,
334+
hExpire: HEXPIRE,
335+
HEXPIREAT,
336+
hExpireAt: HEXPIREAT,
337+
HEXPIRETIME,
338+
hExpireTime: HEXPIRETIME,
324339
HGET,
325340
hGet: HGET,
326341
HGETALL,
@@ -335,6 +350,16 @@ export default {
335350
hLen: HLEN,
336351
HMGET,
337352
hmGet: HMGET,
353+
HPERSIST,
354+
hPersist: HPERSIST,
355+
HPEXPIRE,
356+
hpExpire: HPEXPIRE,
357+
HPEXPIREAT,
358+
hpExpireAt: HPEXPIREAT,
359+
HPEXPIRETIME,
360+
hpExpireTime: HPEXPIRETIME,
361+
HPTTL,
362+
hpTTL: HPTTL,
338363
HRANDFIELD_COUNT_WITHVALUES,
339364
hRandFieldCountWithValues: HRANDFIELD_COUNT_WITHVALUES,
340365
HRANDFIELD_COUNT,
@@ -349,6 +374,8 @@ export default {
349374
hSetNX: HSETNX,
350375
HSTRLEN,
351376
hStrLen: HSTRLEN,
377+
HTTL,
378+
hTTL: HTTL,
352379
HVALS,
353380
hVals: HVALS,
354381
INCR,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { strict as assert } from 'node:assert';
2+
import testUtils, { GLOBAL } from '../test-utils';
3+
import { transformArguments } from './HEXPIRE';
4+
import { HASH_EXPIRATION_TIME } from './HEXPIRETIME';
5+
6+
describe('HEXPIRE', () => {
7+
testUtils.isVersionGreaterThanHook([7, 4]);
8+
9+
describe('transformArguments', () => {
10+
it('string', () => {
11+
assert.deepEqual(
12+
transformArguments('key', 'field', 1),
13+
['HEXPIRE', 'key', '1', 'FIELDS', '1', 'field']
14+
);
15+
});
16+
17+
it('array', () => {
18+
assert.deepEqual(
19+
transformArguments('key', ['field1', 'field2'], 1),
20+
['HEXPIRE', 'key', '1', 'FIELDS', '2', 'field1', 'field2']
21+
);
22+
});
23+
24+
it('with set option', () => {
25+
assert.deepEqual(
26+
transformArguments('key', ['field1'], 1, 'NX'),
27+
['HEXPIRE', 'key', '1', 'NX', 'FIELDS', '1', 'field1']
28+
);
29+
});
30+
});
31+
32+
testUtils.testWithClient('hexpire', async client => {
33+
assert.deepEqual(
34+
await client.hExpire('key', ['field1'], 0),
35+
[ HASH_EXPIRATION_TIME.FieldNotExists ]
36+
);
37+
}, {
38+
...GLOBAL.SERVERS.OPEN
39+
});
40+
});
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { RedisCommandArgument } from '.';
2+
import { pushVerdictArgument } from './generic-transformers';
3+
4+
/**
5+
* @readonly
6+
* @enum {number}
7+
*/
8+
export const HASH_EXPIRATION = {
9+
/** @property {number} */
10+
/** The field does not exist */
11+
FieldNotExists: -2,
12+
/** @property {number} */
13+
/** Specified NX | XX | GT | LT condition not met */
14+
ConditionNotMet: 0,
15+
/** @property {number} */
16+
/** Expiration time was set or updated */
17+
Updated: 1,
18+
/** @property {number} */
19+
/** Field deleted because the specified expiration time is in the past */
20+
Deleted: 2
21+
} as const;
22+
23+
export type HashExpiration = typeof HASH_EXPIRATION[keyof typeof HASH_EXPIRATION];
24+
25+
export const FIRST_KEY_INDEX = 1;
26+
27+
export function transformArguments(
28+
key: RedisCommandArgument,
29+
fields: RedisCommandArgument| Array<RedisCommandArgument>,
30+
seconds: number,
31+
mode?: 'NX' | 'XX' | 'GT' | 'LT',
32+
) {
33+
const args = ['HEXPIRE', key, seconds.toString()];
34+
35+
if (mode) {
36+
args.push(mode);
37+
}
38+
39+
args.push('FIELDS');
40+
41+
return pushVerdictArgument(args, fields);
42+
}
43+
44+
export declare function transformReply(): Array<HashExpiration>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { strict as assert } from 'node:assert';
2+
import testUtils, { GLOBAL } from '../test-utils';
3+
import { transformArguments } from './HEXPIREAT';
4+
import { HASH_EXPIRATION_TIME } from './HEXPIRETIME';
5+
6+
describe('HEXPIREAT', () => {
7+
testUtils.isVersionGreaterThanHook([7, 4]);
8+
9+
describe('transformArguments', () => {
10+
it('string + number', () => {
11+
assert.deepEqual(
12+
transformArguments('key', 'field', 1),
13+
['HEXPIREAT', 'key', '1', 'FIELDS', '1', 'field']
14+
);
15+
});
16+
17+
it('array + number', () => {
18+
assert.deepEqual(
19+
transformArguments('key', ['field1', 'field2'], 1),
20+
['HEXPIREAT', 'key', '1', 'FIELDS', '2', 'field1', 'field2']
21+
);
22+
});
23+
24+
it('date', () => {
25+
const d = new Date();
26+
27+
assert.deepEqual(
28+
transformArguments('key', ['field1'], d),
29+
['HEXPIREAT', 'key', Math.floor(d.getTime() / 1000).toString(), 'FIELDS', '1', 'field1']
30+
);
31+
});
32+
33+
it('with set option', () => {
34+
assert.deepEqual(
35+
transformArguments('key', 'field1', 1, 'GT'),
36+
['HEXPIREAT', 'key', '1', 'GT', 'FIELDS', '1', 'field1']
37+
);
38+
});
39+
});
40+
41+
testUtils.testWithClient('expireAt', async client => {
42+
assert.deepEqual(
43+
await client.hExpireAt('key', 'field1', 1),
44+
[ HASH_EXPIRATION_TIME.FieldNotExists ]
45+
);
46+
}, {
47+
...GLOBAL.SERVERS.OPEN,
48+
});
49+
});
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { RedisCommandArgument } from '.';
2+
import { pushVerdictArgument, transformEXAT } from './generic-transformers';
3+
import { HashExpiration } from './HEXPIRE';
4+
5+
export const FIRST_KEY_INDEX = 1;
6+
7+
export function transformArguments(
8+
key: RedisCommandArgument,
9+
fields: RedisCommandArgument | Array<RedisCommandArgument>,
10+
timestamp: number | Date,
11+
mode?: 'NX' | 'XX' | 'GT' | 'LT'
12+
) {
13+
const args = [
14+
'HEXPIREAT',
15+
key,
16+
transformEXAT(timestamp)
17+
];
18+
19+
if (mode) {
20+
args.push(mode);
21+
}
22+
23+
args.push('FIELDS')
24+
25+
return pushVerdictArgument(args, fields);
26+
}
27+
28+
export declare function transformReply(): Array<HashExpiration>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { strict as assert } from 'node:assert';
2+
import testUtils, { GLOBAL } from '../test-utils';
3+
import { HASH_EXPIRATION_TIME, transformArguments } from './HEXPIRETIME';
4+
5+
describe('HEXPIRETIME', () => {
6+
testUtils.isVersionGreaterThanHook([7, 4]);
7+
8+
describe('transformArguments', () => {
9+
it('string', () => {
10+
assert.deepEqual(
11+
transformArguments('key', 'field'),
12+
['HEXPIRETIME', 'key', 'FIELDS', '1', 'field']
13+
);
14+
});
15+
16+
it('array', () => {
17+
assert.deepEqual(
18+
transformArguments('key', ['field1', 'field2']),
19+
['HEXPIRETIME', 'key', 'FIELDS', '2', 'field1', 'field2']
20+
);
21+
});
22+
})
23+
24+
testUtils.testWithClient('hExpireTime', async client => {
25+
assert.deepEqual(
26+
await client.hExpireTime('key', 'field1'),
27+
[ HASH_EXPIRATION_TIME.FieldNotExists ]
28+
);
29+
}, {
30+
...GLOBAL.SERVERS.OPEN,
31+
});
32+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { RedisCommandArgument } from '.';
2+
import { pushVerdictArgument } from './generic-transformers';
3+
4+
export const HASH_EXPIRATION_TIME = {
5+
/** @property {number} */
6+
/** The field does not exist */
7+
FieldNotExists: -2,
8+
/** @property {number} */
9+
/** The field exists but has no associated expire */
10+
NoExpiration: -1,
11+
} as const;
12+
13+
export const FIRST_KEY_INDEX = 1
14+
15+
export const IS_READ_ONLY = true;
16+
17+
export function transformArguments(key: RedisCommandArgument, fields: RedisCommandArgument | Array<RedisCommandArgument>) {
18+
return pushVerdictArgument(['HEXPIRETIME', key, 'FIELDS'], fields);
19+
}
20+
21+
export declare function transformReply(): Array<number>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { strict as assert } from 'node:assert';
2+
import testUtils, { GLOBAL } from '../test-utils';
3+
import { transformArguments } from './HPERSIST';
4+
import { HASH_EXPIRATION_TIME } from './HEXPIRETIME';
5+
6+
describe('HPERSIST', () => {
7+
testUtils.isVersionGreaterThanHook([7, 4]);
8+
9+
describe('transformArguments', () => {
10+
it('string', () => {
11+
assert.deepEqual(
12+
transformArguments('key', 'field'),
13+
['HPERSIST', 'key', 'FIELDS', '1', 'field']
14+
);
15+
});
16+
17+
it('array', () => {
18+
assert.deepEqual(
19+
transformArguments('key', ['field1', 'field2']),
20+
['HPERSIST', 'key', 'FIELDS', '2', 'field1', 'field2']
21+
);
22+
});
23+
})
24+
25+
testUtils.testWithClient('hPersist', async client => {
26+
assert.deepEqual(
27+
await client.hPersist('key', 'field1'),
28+
[ HASH_EXPIRATION_TIME.FieldNotExists ]
29+
);
30+
}, {
31+
...GLOBAL.SERVERS.OPEN,
32+
});
33+
});
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { RedisCommandArgument } from '.';
2+
import { pushVerdictArgument } from './generic-transformers';
3+
4+
export const FIRST_KEY_INDEX = 1;
5+
6+
export function transformArguments(key: RedisCommandArgument, fields: RedisCommandArgument | Array<RedisCommandArgument>) {
7+
return pushVerdictArgument(['HPERSIST', key, 'FIELDS'], fields);
8+
}
9+
10+
export declare function transformReply(): Array<number> | null;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { strict as assert } from 'node:assert';
2+
import testUtils, { GLOBAL } from '../test-utils';
3+
import { transformArguments } from './HPEXPIRE';
4+
import { HASH_EXPIRATION_TIME } from './HEXPIRETIME';
5+
6+
describe('HEXPIRE', () => {
7+
testUtils.isVersionGreaterThanHook([7, 4]);
8+
9+
describe('transformArguments', () => {
10+
it('string', () => {
11+
assert.deepEqual(
12+
transformArguments('key', 'field', 1),
13+
['HPEXPIRE', 'key', '1', 'FIELDS', '1', 'field']
14+
);
15+
});
16+
17+
it('array', () => {
18+
assert.deepEqual(
19+
transformArguments('key', ['field1', 'field2'], 1),
20+
['HPEXPIRE', 'key', '1', 'FIELDS', '2', 'field1', 'field2']
21+
);
22+
});
23+
24+
it('with set option', () => {
25+
assert.deepEqual(
26+
transformArguments('key', ['field1'], 1, 'NX'),
27+
['HPEXPIRE', 'key', '1', 'NX', 'FIELDS', '1', 'field1']
28+
);
29+
});
30+
});
31+
32+
testUtils.testWithClient('hexpire', async client => {
33+
assert.deepEqual(
34+
await client.hpExpire('key', ['field1'], 0),
35+
[ HASH_EXPIRATION_TIME.FieldNotExists ]
36+
);
37+
}, {
38+
...GLOBAL.SERVERS.OPEN
39+
});
40+
});

0 commit comments

Comments
 (0)