From e1c2b9133cb7cf42d8d269529c90619552d17868 Mon Sep 17 00:00:00 2001 From: nidhidham <125385585+nidhidham@users.noreply.github.com> Date: Tue, 14 Mar 2023 09:21:32 -0700 Subject: [PATCH] feat: cache flush (#345) * feat: cache flush * feat: cache flush --- package-lock.json | 14 +++--- package.json | 2 +- src/cache-client.ts | 13 +++++ src/index.ts | 2 + src/internal/control-client.ts | 34 +++++++++++++ src/messages/responses/cache-flush.ts | 48 +++++++++++++++++++ .../create-delete-list-cache.test.ts | 46 ++++++++++++++++++ 7 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 src/messages/responses/cache-flush.ts diff --git a/package-lock.json b/package-lock.json index ef48707d0..42d195ae5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.1", "license": "Apache-2.0", "dependencies": { - "@gomomento/generated-types": "0.49.1", + "@gomomento/generated-types": "0.50.0", "@grpc/grpc-js": "1.7.3", "google-protobuf": "^3.21.2", "jwt-decode": "3.1.2" @@ -737,9 +737,9 @@ } }, "node_modules/@gomomento/generated-types": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@gomomento/generated-types/-/generated-types-0.49.1.tgz", - "integrity": "sha512-j0jdhl4M6HJOWcDOKupGse74NEsFifqe2lirXsSKfc++1JIuFazkt6GO+ZtgRDlqP60S9B5ZX1kTpb0mBSd0tw==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@gomomento/generated-types/-/generated-types-0.50.0.tgz", + "integrity": "sha512-QiEYOG5hoJHSIndCkIiN6r4NwQZBNjy1zmUk83kkxlDk+9sQQmVTADH8VAnv73qizVqTZXfrOsUjnPwsLhrXZQ==", "dependencies": { "@grpc/grpc-js": "1.7.3", "@types/google-protobuf": "3.15.5", @@ -7166,9 +7166,9 @@ } }, "@gomomento/generated-types": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@gomomento/generated-types/-/generated-types-0.49.1.tgz", - "integrity": "sha512-j0jdhl4M6HJOWcDOKupGse74NEsFifqe2lirXsSKfc++1JIuFazkt6GO+ZtgRDlqP60S9B5ZX1kTpb0mBSd0tw==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@gomomento/generated-types/-/generated-types-0.50.0.tgz", + "integrity": "sha512-QiEYOG5hoJHSIndCkIiN6r4NwQZBNjy1zmUk83kkxlDk+9sQQmVTADH8VAnv73qizVqTZXfrOsUjnPwsLhrXZQ==", "requires": { "@grpc/grpc-js": "1.7.3", "@types/google-protobuf": "3.15.5", diff --git a/package.json b/package.json index 84c230aba..8b26d5112 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "uuid": "8.3.2" }, "dependencies": { - "@gomomento/generated-types": "0.49.1", + "@gomomento/generated-types": "0.50.0", "@grpc/grpc-js": "1.7.3", "google-protobuf": "^3.21.2", "jwt-decode": "3.1.2" diff --git a/src/cache-client.ts b/src/cache-client.ts index 9b0269e92..d1cb741a1 100644 --- a/src/cache-client.ts +++ b/src/cache-client.ts @@ -46,6 +46,7 @@ import { CacheSortedSetPutElement, CacheSortedSetPutElements, MomentoLogger, + CacheFlush, } from '.'; import {range} from './internal/utils/collections'; import {CacheClientProps} from './cache-client-props'; @@ -591,6 +592,18 @@ export class CacheClient { return await this.controlClient.deleteCache(cacheName); } + /** + * Flushes / clears all the items of the given cache + * + * @param {string} cacheName - The cache to be flushed. + * @returns {Promise} - + * {@link CacheFlush.Success} on success. + * {@link CacheFlush.Error} on failure. + */ + public async flushCache(cacheName: string): Promise { + return await this.controlClient.flushCache(cacheName); + } + /** * Lists all caches. * diff --git a/src/index.ts b/src/index.ts index 57657fba0..ffccbca6f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ import * as CacheListPushFront from './messages/responses/cache-list-push-front' import * as CacheListRemoveValue from './messages/responses/cache-list-remove-value'; import * as CacheSet from './messages/responses/cache-set'; import * as CacheDelete from './messages/responses/cache-delete'; +import * as CacheFlush from './messages/responses/cache-flush'; import * as CreateCache from './messages/responses/create-cache'; import * as DeleteCache from './messages/responses/delete-cache'; import * as ListCaches from './messages/responses/list-caches'; @@ -109,6 +110,7 @@ export { CacheSet, CacheSetIfNotExists, CacheDelete, + CacheFlush, CreateCache, DeleteCache, ListCaches, diff --git a/src/internal/control-client.ts b/src/internal/control-client.ts index 338b55821..d3530f34e 100644 --- a/src/internal/control-client.ts +++ b/src/internal/control-client.ts @@ -15,6 +15,7 @@ import { ListSigningKeys, RevokeSigningKey, MomentoLogger, + CacheFlush, } from '..'; import {version} from '../../package.json'; import {IdleGrpcClientWrapper} from './grpc/idle-grpc-client-wrapper'; @@ -115,6 +116,39 @@ export class ControlClient { }); } + public async flushCache(cacheName: string): Promise { + try { + validateCacheName(cacheName); + } catch (err) { + return new CacheFlush.Error(normalizeSdkError(err as Error)); + } + this.logger.trace(`Flushing cache: ${cacheName}`); + return await this.sendFlushCache(cacheName); + } + + private async sendFlushCache( + cacheName: string + ): Promise { + const request = new grpcControl._FlushCacheRequest({ + cache_name: cacheName, + }); + return await new Promise(resolve => { + this.clientWrapper.getClient().FlushCache( + request, + { + interceptors: this.interceptors, + }, + (err, resp) => { + if (resp) { + resolve(new CacheFlush.Success()); + } else { + resolve(new CacheFlush.Error(cacheServiceErrorMapper(err))); + } + } + ); + }); + } + public async listCaches(): Promise { const request = new grpcControl._ListCachesRequest(); request.next_token = ''; diff --git a/src/messages/responses/cache-flush.ts b/src/messages/responses/cache-flush.ts new file mode 100644 index 000000000..b05005b2e --- /dev/null +++ b/src/messages/responses/cache-flush.ts @@ -0,0 +1,48 @@ +import {SdkError} from '../../errors/errors'; +import {ResponseBase, ResponseError, ResponseSuccess} from './response-base'; + +/** + * Parent response type for a flush cache request. The + * response object is resolved to a type-safe object of one of + * the following subtypes: + * + * - {Success} + * - {Error} + * + * `instanceof` type guards can be used to operate on the appropriate subtype. + * @example + * For example: + * ``` + * if (response instanceof CacheFlush.Error) { + * // Handle error as appropriate. The compiler will smart-cast `response` to type + * // `CacheFlush.Error` in this block, so you will have access to the properties + * // of the Error class; e.g. `response.errorCode()`. + * } + * ``` + */ +export abstract class Response extends ResponseBase {} + +class _Success extends Response {} + +/** + * Indicates a Successful cache flush request. + */ +export class Success extends ResponseSuccess(_Success) {} + +class _Error extends Response { + constructor(protected _innerException: SdkError) { + super(); + } +} + +/** + * Indicates that an error occurred during the cache flush request. + * + * This response object includes the following fields that you can use to determine + * how you would like to handle the error: + * + * - `errorCode()` - a unique Momento error code indicating the type of error that occurred. + * - `message()` - a human-readable description of the error + * - `innerException()` - the original error that caused the failure; can be re-thrown. + */ +export class Error extends ResponseError(_Error) {} diff --git a/test/integration/create-delete-list-cache.test.ts b/test/integration/create-delete-list-cache.test.ts index 5dd373a97..c5c0a8c0d 100644 --- a/test/integration/create-delete-list-cache.test.ts +++ b/test/integration/create-delete-list-cache.test.ts @@ -6,6 +6,8 @@ import { WithCache, } from './integration-setup'; import { + CacheFlush, + CacheGet, CreateCache, DeleteCache, ListCaches, @@ -56,3 +58,47 @@ describe('create/delete cache', () => { }); }); }); + +describe('flush cache', () => { + ItBehavesLikeItValidatesCacheName((props: ValidateCacheProps) => { + return Momento.flushCache(props.cacheName); + }); + + it('should return NotFoundError if flushing a non-existent cache', async () => { + const cacheName = v4(); + const flushResponse = await Momento.flushCache(cacheName); + expect(flushResponse).toBeInstanceOf(CacheFlush.Response); + expect(flushResponse).toBeInstanceOf(CacheFlush.Error); + if (flushResponse instanceof CacheFlush.Error) { + expect(flushResponse.errorCode()).toEqual( + MomentoErrorCode.NOT_FOUND_ERROR + ); + } + }); + + it('should return success while flushing empty cache', async () => { + const cacheName = v4(); + await Momento.createCache(cacheName); + const flushResponse = await Momento.flushCache(cacheName); + expect(flushResponse).toBeInstanceOf(CacheFlush.Response); + expect(flushResponse).toBeInstanceOf(CacheFlush.Success); + }); + + it('should return success while flushing non-empty cache', async () => { + const cacheName = v4(); + const key1 = v4(); + const key2 = v4(); + const value1 = v4(); + const value2 = v4(); + await Momento.createCache(cacheName); + await Momento.set(cacheName, key1, value1); + await Momento.set(cacheName, key2, value2); + const flushResponse = await Momento.flushCache(cacheName); + expect(flushResponse).toBeInstanceOf(CacheFlush.Response); + expect(flushResponse).toBeInstanceOf(CacheFlush.Success); + const getResponse1 = await Momento.get(cacheName, key1); + const getResponse2 = await Momento.get(cacheName, key2); + expect(getResponse1).toBeInstanceOf(CacheGet.Miss); + expect(getResponse2).toBeInstanceOf(CacheGet.Miss); + }); +});