Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add incr api #28

Merged
merged 2 commits into from
May 2, 2024
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
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
test-momento:
strategy:
matrix:
node: [14, 16, 18]
node: [16, 18, 20]
name: Test on Node ${{ matrix.node }}
runs-on: macos-latest
env:
Expand Down Expand Up @@ -59,7 +59,7 @@ jobs:
# TODO the redis tests are flaky when run all in parallel.
max-parallel: 2
matrix:
node: [14, 16, 18]
node: [16, 18, 20]
name: Test on Node ${{ matrix.node }}
runs-on: ubuntu-latest
services:
Expand Down
30 changes: 29 additions & 1 deletion src/momento-redis-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
CacheDictionaryGetField,
CacheDictionaryGetFields,
SdkError,
CacheIncrement,
} from '@gomomento/sdk';

import {ClientCommandOptions} from '@redis/client/dist/lib/client';
Expand Down Expand Up @@ -50,6 +51,7 @@ type HMGetParams = [
type HSetParams = Parameters<
(typeof RedisCommands)['HSET']['transformArguments']
>;
type IncrementParams = [key: RedisCommandArgument];

type CommandParams =
| GetParams
Expand All @@ -59,7 +61,8 @@ type CommandParams =
| HSetParams
| HGetAllParams
| HMGetParams
| HGetParams;
| HGetParams
| IncrementParams;
type WithOptionalOptions<T extends CommandParams> =
| T
| [options: ClientCommandOptions, ...args: T];
Expand Down Expand Up @@ -102,6 +105,8 @@ export interface IMomentoRedisClient {
HMGET: IMomentoRedisClient['hmGet'];
hSet(...args: WithOptionalOptions<HSetParams>): Promise<number>;
HSET: IMomentoRedisClient['hSet'];
incr(...args: WithOptionalOptions<IncrementParams>): Promise<number | null>;
INCR: IMomentoRedisClient['incr'];
}

export class MomentoRedisClient
Expand Down Expand Up @@ -481,6 +486,29 @@ export class MomentoRedisClient
return 0;
}

public async incr(
...args: WithOptionalOptions<IncrementParams>
): Promise<number | null> {
const [, otherArgs] =
MomentoRedisClient.extractReturnBuffersOptionFromArgs(args);
return await this.sendIncr(otherArgs as IncrementParams);
}

// eslint-disable-next-line @typescript-eslint/unbound-method
public INCR = this.incr;

private async sendIncr([key]: IncrementParams): Promise<number | null> {
const response = await this.client.increment(this.cacheName, key);
if (response instanceof CacheIncrement.Success) {
return response.value();
} else if (response instanceof CacheIncrement.Error) {
this.emitError(response.innerException());
} else {
this.emitError(UNEXPECTED_RESPONSE);
}
return null;
}

private emitError(error: SdkError | ErrorReply): void {
const errorReply =
error instanceof ErrorReply
Expand Down
62 changes: 62 additions & 0 deletions test/increment.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {SetupIntegrationTest} from './integration-setup';
import {v4} from 'uuid';

const {client} = SetupIntegrationTest();

describe('increment', () => {
it('should increment the value of the key by 1 if the key exists', async () => {
const key = v4();
const value = 5;
// set initial key value
await client.set(key, value);
// increment key value
const incrementResult = await client.incr(key);
expect(incrementResult).toEqual(value + 1);
});

it('should increment the value of the key to 1 if the key does not exists', async () => {
const key = v4();
// increment key value
const incrementResult = await client.incr(key);
expect(incrementResult).toEqual(1);
});

it('should error out if the key contains a value of wrong type or contains a string that can be represented as integer', async () => {
const key = v4();
const value = 'monkey';
// Set initial key value
await client.set(key, value);
// Increment the value of the key that is not set
try {
await client.incr(key);
} catch (error) {
if (process.env.TEST_REDIS === 'false') {
const momentoError = error as {
code: string;
context: {code: string; msg: string; op: string; platform: string};
};
expect(momentoError.code).toBe('ERR_UNHANDLED_ERROR');
expect(momentoError.context.code).toBe('FAILED_PRECONDITION_ERROR');
expect(momentoError.context.msg).toBe(
"System is not in a state required for the operation's execution: 9 FAILED_PRECONDITION: failed to parse value into long"
);
expect(momentoError.context.op).toBe('incr');
expect(momentoError.context.platform).toBe('momento');
} else if (process.env.TEST_REDIS === 'true') {
expect((error as Error).message).toBe(
'ERR value is not an integer or out of range'
);
}
}
});

it('should increment the value of key that contains a string that can be represented as integer', async () => {
const key = v4();
const value = '10';
// Set initial key value
await client.set(key, value);
// Increment the value of the key that is not set
const incrResp = await client.incr(key);
expect(incrResp).toBe(11);
});
});
Loading