Skip to content

Add .exists() Method to Repository #263

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
6 changes: 6 additions & 0 deletions lib/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@ export class Client {
this.#validateRedisOpen()
await this.redis.expireAt(key, timestamp)
}

/** @internal */
async exists(...keys: string[]) {
this.#validateRedisOpen()
return this.redis.exists(keys);
}

/** @internal */
async get(key: string): Promise<string | null> {
Expand Down
28 changes: 28 additions & 0 deletions lib/repository/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,34 @@ export class Repository<T extends Entity = Record<string, any>> {
);
}

/**
* Returns boolean representing existence of an {@link Entity} in Redis for the given id.
*
* @param id The ID of the {@link Entity} you wish to check for existence.
*/
async exists(id: string): Promise<boolean>

/**
* Returns boolean representing existence of {@link Entity | Entities} in Redis. Returns `true` if ALL provided IDs exist otherwise `false`.
*
* @param ids The IDs of the {@link Entity | Entities} you wish to check for existence.
*/
async exists(...ids: string[]): Promise<boolean>

/**
* Returns boolean representing existence of {@link Entity | Entities} in Redis. Returns `true` if ALL provided IDs exist otherwise `false`.
*
* @param ids The IDs of the {@link Entity | Entities} you wish to check for existence.
*/
async exists(ids: string[]): Promise<boolean>

async exists(idOrIds: string | string[]): Promise<boolean> {
const keys = Array.isArray(idOrIds) ? this.makeKeys([...new Set(idOrIds).values()]) : this.makeKeys([idOrIds]);
const numberOfKeysThatExist = await this.client.exists(...keys);
return numberOfKeysThatExist === keys.length;

}

/**
* Kicks off the process of building a query. Requires that RediSearch (and optionally
* RedisJSON) be installed on your instance of Redis.
Expand Down
41 changes: 41 additions & 0 deletions spec/unit/client/client-exists.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import '../../helpers/custom-matchers'

import { redis } from '../helpers/mock-redis'

import { Client } from '$lib/client'
import { RedisOmError } from '$lib/error'

describe("Client", () => {

let client: Client

beforeEach(() => { client = new Client() })

describe("#exists", () => {
describe("when called on an open client", () => {
beforeEach(async () => {
await client.open()
})

it("passes the command to redis", async () => {
await client.exists('foo')
expect(redis.exists).toHaveBeenCalledWith(['foo'])
})
})

describe("when called on a closed client", () => {
beforeEach(async () => {
await client.open()
await client.close()
})

it("errors when called on a closed client", () =>
expect(async () => await client.exists('foo'))
.rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open."))
})

it("errors when called on a new client", async () =>
expect(async () => await client.exists('foo'))
.rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open."))
})
})
1 change: 1 addition & 0 deletions spec/unit/helpers/mock-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ client.search = vi.fn()
client.unlink = vi.fn()
client.expire = vi.fn()
client.expireAt = vi.fn()
client.exists = vi.fn()
client.get = vi.fn()
client.set = vi.fn()
client.hgetall = vi.fn()
Expand Down
1 change: 1 addition & 0 deletions spec/unit/helpers/mock-redis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const redis = {
hGetAll: vi.fn(),
expire: vi.fn(),
expireAt: vi.fn(),
exists: vi.fn(),
sendCommand: vi.fn(),
unlink: vi.fn(),
multi: vi.fn().mockImplementation(() => multi)
Expand Down
37 changes: 37 additions & 0 deletions spec/unit/repository/repository-exists.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import '../helpers/mock-client';

import { Client } from '$lib/client';
import { Repository } from '$lib/repository';
import { Schema } from '$lib/schema';

const simpleSchema = new Schema('SimpleEntity', {}, { dataStructure: 'HASH' });

describe('Repository', () => {
describe('#exists', () => {
let client: Client;
let repository: Repository;

beforeAll(() => {
client = new Client();
});
beforeEach(() => {
repository = new Repository(simpleSchema, client);
});

it('checks existence of a single entity', async () => {
await repository.exists('foo');
expect(client.exists).toHaveBeenCalledWith('SimpleEntity:foo');
});

it('checks existence of multiple entities', async () => {
await repository.exists(['foo', 'bar', 'baz']);
expect(client.exists).toHaveBeenCalledWith('SimpleEntity:foo', 'SimpleEntity:bar', 'SimpleEntity:baz');
});

it('removes duplicate keys prior to checking for existence of entities', async () => {
await repository.exists(['foo', 'bar', 'baz', 'bar']);
expect(client.exists).toHaveBeenCalledWith('SimpleEntity:foo', 'SimpleEntity:bar', 'SimpleEntity:baz');
});

});
});