Skip to content

Commit

Permalink
chore: add record accessor for keys exist response (#1263)
Browse files Browse the repository at this point in the history
We add a record accessor for keys exist to be in line with the .NET
and Rust SDKs.

The record maps from the key to a boolean indicating whether the key
exists in the cache or not.
  • Loading branch information
malandis authored May 3, 2024
1 parent b988b7b commit 307d81f
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4582,7 +4582,7 @@ export class CacheDataClient implements IDataClient {
},
(err, resp) => {
if (resp) {
resolve(new CacheKeysExist.Success(resp.exists));
resolve(new CacheKeysExist.Success(keys, resp.exists));
} else {
this.cacheServiceErrorMapper.resolveOrRejectError({
err: err,
Expand Down
16 changes: 9 additions & 7 deletions packages/client-sdk-web/src/internal/cache-data-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3987,10 +3987,7 @@ export class CacheDataClient<

this.logger.trace("Issuing 'keysExist' request");

const result = await this.sendKeysExist(
cacheName,
this.convertArrayToB64Strings(keys)
);
const result = await this.sendKeysExist(cacheName, keys);

this.logger.trace(
"'keysExist' request result: %s",
Expand All @@ -4001,10 +3998,10 @@ export class CacheDataClient<

private async sendKeysExist(
cacheName: string,
keys: string[]
keys: string[] | Uint8Array[]
): Promise<CacheKeysExist.Response> {
const request = new _KeysExistRequest();
request.setCacheKeysList(keys);
request.setCacheKeysList(this.convertArrayToB64Strings(keys));

return await new Promise((resolve, reject) => {
this.clientWrapper.keysExist(
Expand All @@ -4015,7 +4012,12 @@ export class CacheDataClient<
},
(err, resp) => {
if (resp) {
resolve(new CacheKeysExist.Success(resp.getExistsList()));
resolve(
new CacheKeysExist.Success(
this.convertArrayToUint8(keys),
resp.getExistsList()
)
);
} else {
this.cacheServiceErrorMapper.resolveOrRejectError({
err: err,
Expand Down
40 changes: 32 additions & 8 deletions packages/common-integration-tests/src/keys-exist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ import {
} from './common-int-test-utils';
import {ICacheClient} from '@gomomento/sdk-core/dist/src/internal/clients/cache';
import {CacheKeyExists, CacheKeysExist} from '@gomomento/sdk-core';

function zipToRecord<V>(keys: string[], values: V[]): Record<string, V> {
return keys.reduce<Record<string, V>>((acc, key, index) => {
acc[key] = values[index];
return acc;
}, {} as Record<string, V>);
}

export function runKeysExistTest(
cacheClient: ICacheClient,
integrationTestCacheName: string
Expand Down Expand Up @@ -107,6 +115,8 @@ export function runKeysExistTest(
await cacheClient.set(integrationTestCacheName, key1, key1);
await cacheClient.set(integrationTestCacheName, key3, key3);

let keysList = [key1, key2, key3];
let existsMask = [true, false, true];
const responseOrdering1 = await cacheClient.keysExist(
integrationTestCacheName,
[key1, key2, key3]
Expand All @@ -116,32 +126,46 @@ export function runKeysExistTest(
expect(responseOrdering1).toBeInstanceOf(CacheKeysExist.Success);
}, `expected SUCCESS but got ${responseOrdering1.toString()}`);

const successOrdering1 = responseOrdering1 as CacheKeyExists.Success;
expect(successOrdering1.exists()).toEqual([true, false, true]);
const successOrdering1 = responseOrdering1 as CacheKeysExist.Success;
expect(successOrdering1.exists()).toEqual(existsMask);

expect(successOrdering1.valueRecord()).toEqual(
zipToRecord(keysList, existsMask)
);

keysList = [key2, key3, key1];
existsMask = [false, true, true];
const responseOrdering2 = await cacheClient.keysExist(
integrationTestCacheName,
[key2, key3, key1]
keysList
);

expectWithMessage(() => {
expect(responseOrdering2).toBeInstanceOf(CacheKeysExist.Success);
}, `expected SUCCESS but got ${responseOrdering2.toString()}`);

const successOrdering2 = responseOrdering2 as CacheKeyExists.Success;
expect(successOrdering2.exists()).toEqual([false, true, true]);
const successOrdering2 = responseOrdering2 as CacheKeysExist.Success;
expect(successOrdering2.exists()).toEqual(existsMask);
expect(successOrdering2.valueRecord()).toEqual(
zipToRecord(keysList, existsMask)
);

keysList = [key2, key4];
existsMask = [false, false];
const responseOrdering3 = await cacheClient.keysExist(
integrationTestCacheName,
[key2, key4]
keysList
);

expectWithMessage(() => {
expect(responseOrdering3).toBeInstanceOf(CacheKeysExist.Success);
}, `expected SUCCESS but got ${responseOrdering3.toString()}`);

const successOrdering3 = responseOrdering3 as CacheKeyExists.Success;
expect(successOrdering3.exists()).toEqual([false, false]);
const successOrdering3 = responseOrdering3 as CacheKeysExist.Success;
expect(successOrdering3.exists()).toEqual(existsMask);
expect(successOrdering3.valueRecord()).toEqual(
zipToRecord(keysList, existsMask)
);
});

it('should support happy path for keysExist via curried cache via ICache interface', async () => {
Expand Down
17 changes: 16 additions & 1 deletion packages/core/src/messages/responses/cache-keys-exist.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {SdkError} from '../../errors';
import {ResponseBase, ResponseError, ResponseSuccess} from './response-base';

const TEXT_DECODER = new TextDecoder();

/**
* Parent response type for a cache keys exist request. The
* response object is resolved to a type-safe object of one of
Expand All @@ -23,10 +25,12 @@ import {ResponseBase, ResponseError, ResponseSuccess} from './response-base';
export abstract class Response extends ResponseBase {}

class _Success extends Response {
private readonly _keys: Uint8Array[];
private readonly _exists: boolean[];

constructor(exists: boolean[]) {
constructor(keys: Uint8Array[], exists: boolean[]) {
super();
this._keys = keys;
this._exists = exists;
}

Expand All @@ -38,6 +42,17 @@ class _Success extends Response {
return this._exists;
}

/**
* A record of key-value pairs indicating whether each given key was found in the cache.
* @returns {Record<string, boolean>}
*/
public valueRecord(): Record<string, boolean> {
return this._keys.reduce<Record<string, boolean>>((acc, field, index) => {
acc[TEXT_DECODER.decode(field)] = this._exists[index];
return acc;
}, {});
}

public override toString(): string {
const booleans = this._exists.map(bool => bool);
return super.toString() + ': exists: ' + booleans.join(', ');
Expand Down

0 comments on commit 307d81f

Please sign in to comment.