Skip to content

Commit

Permalink
Implemented repository.expiredAt method
Browse files Browse the repository at this point in the history
  • Loading branch information
garrett-green committed Sep 16, 2023
1 parent 3ffbedb commit 0c09089
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 0 deletions.
37 changes: 37 additions & 0 deletions lib/repository/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,43 @@ export class Repository {
)
}

/**
* Use Date object to set the {@link Entity}'s time to live. If the {@link Entity}
* is not found, does nothing.
*
* @param id The ID of the {@link Entity} to set an expiration date for.
* @param expirationDate The time the data should expire.
*/
async expireAt(id: string, expirationDate: Date): Promise<void>;

/**
* Use Date object to set the {@link Entity | Entities} in Redis with the given
* ids. If a particular {@link Entity} is not found, does nothing.
*
* @param ids The IDs of the {@link Entity | Entities} to set an expiration date for.
* @param expirationDate The time the data should expire.
*/
async expireAt(ids: string[], expirationDate: Date): Promise<void>;

async expireAt(idOrIds: string | string[], expirationDate: Date) {
const ids = typeof idOrIds === 'string' ? [idOrIds] : idOrIds;
const timeNow: number = Date.now();
if (Date.now() >= expirationDate.getTime()) {
throw new Error(
`${expirationDate.toString()} is invalid. Expiration date must be in the future.`
);
}
const ttlInSeconds: number = Math.round(
(expirationDate.getTime() - timeNow) / 1000
);
await Promise.all(
ids.map((id) => {
const key = this.makeKey(id);
return this.client.expire(key, ttlInSeconds);
})
);
}

/**
* Kicks off the process of building a query. Requires that RediSearch (and optionally
* RedisJSON) be installed on your instance of Redis.
Expand Down
72 changes: 72 additions & 0 deletions spec/unit/repository/repository-expireAt.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
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' });

const today = new Date();
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
const yesterday = new Date(today);
yesterday.setDate(yesterday.getDate() - 1);

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

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

it('expires a single entity', async () => {
const ttlInSeconds: number = Math.round(
(tomorrow.getTime() - Date.now()) / 1000
);
await repository.expireAt('foo', tomorrow);
expect(client.expire).toHaveBeenCalledWith(
'SimpleEntity:foo',
ttlInSeconds
);
});

it('expires multiple entities', async () => {
const ttlInSeconds: number = Math.round(
(tomorrow.getTime() - Date.now()) / 1000
);
await repository.expireAt(['foo', 'bar', 'baz'], tomorrow);
expect(client.expire).toHaveBeenNthCalledWith(
1,
'SimpleEntity:foo',
ttlInSeconds
);
expect(client.expire).toHaveBeenNthCalledWith(
2,
'SimpleEntity:bar',
ttlInSeconds
);
expect(client.expire).toHaveBeenNthCalledWith(
3,
'SimpleEntity:baz',
ttlInSeconds
);
});

it('throws an error when provided invalid/past date', async () => {
let caughtError: any;
await repository.expireAt('foo', yesterday).catch((error) => {
caughtError = error;
});
expect(client.expire).toHaveBeenCalledTimes(0);
expect(caughtError).toBeDefined();
expect(caughtError!.message).toEqual(
`${yesterday.toString()} is invalid. Expiration date must be in the future.`
);
});
});
});

0 comments on commit 0c09089

Please sign in to comment.