mirror of
https://github.com/DeBrosOfficial/network-ts-sdk.git
synced 2025-12-11 01:58:49 +00:00
Update package version to 0.3.0 and introduce CacheClient with caching functionality
- Added CacheClient to manage cache operations including get, put, delete, and scan. - Updated createClient function to include cache client. - Added new types and interfaces for cache requests and responses. - Implemented comprehensive tests for cache functionality, covering health checks, value storage, retrieval, deletion, and scanning.
This commit is contained in:
parent
272c6b872c
commit
e30e81d0c9
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@debros/network-ts-sdk",
|
||||
"version": "0.2.5",
|
||||
"version": "0.3.0",
|
||||
"description": "TypeScript SDK for DeBros Network Gateway",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
114
src/cache/client.ts
vendored
Normal file
114
src/cache/client.ts
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
import { HttpClient } from "../core/http";
|
||||
|
||||
export interface CacheGetRequest {
|
||||
dmap: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
export interface CacheGetResponse {
|
||||
key: string;
|
||||
value: any;
|
||||
dmap: string;
|
||||
}
|
||||
|
||||
export interface CachePutRequest {
|
||||
dmap: string;
|
||||
key: string;
|
||||
value: any;
|
||||
ttl?: string; // Duration string like "1h", "30m"
|
||||
}
|
||||
|
||||
export interface CachePutResponse {
|
||||
status: string;
|
||||
key: string;
|
||||
dmap: string;
|
||||
}
|
||||
|
||||
export interface CacheDeleteRequest {
|
||||
dmap: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
export interface CacheDeleteResponse {
|
||||
status: string;
|
||||
key: string;
|
||||
dmap: string;
|
||||
}
|
||||
|
||||
export interface CacheScanRequest {
|
||||
dmap: string;
|
||||
match?: string; // Optional regex pattern
|
||||
}
|
||||
|
||||
export interface CacheScanResponse {
|
||||
keys: string[];
|
||||
count: number;
|
||||
dmap: string;
|
||||
}
|
||||
|
||||
export interface CacheHealthResponse {
|
||||
status: string;
|
||||
service: string;
|
||||
}
|
||||
|
||||
export class CacheClient {
|
||||
private httpClient: HttpClient;
|
||||
|
||||
constructor(httpClient: HttpClient) {
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check cache service health
|
||||
*/
|
||||
async health(): Promise<CacheHealthResponse> {
|
||||
return this.httpClient.get("/v1/cache/health");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value from cache
|
||||
*/
|
||||
async get(dmap: string, key: string): Promise<CacheGetResponse> {
|
||||
return this.httpClient.post<CacheGetResponse>("/v1/cache/get", {
|
||||
dmap,
|
||||
key,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value into cache
|
||||
*/
|
||||
async put(
|
||||
dmap: string,
|
||||
key: string,
|
||||
value: any,
|
||||
ttl?: string
|
||||
): Promise<CachePutResponse> {
|
||||
return this.httpClient.post<CachePutResponse>("/v1/cache/put", {
|
||||
dmap,
|
||||
key,
|
||||
value,
|
||||
ttl,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a value from cache
|
||||
*/
|
||||
async delete(dmap: string, key: string): Promise<CacheDeleteResponse> {
|
||||
return this.httpClient.post<CacheDeleteResponse>("/v1/cache/delete", {
|
||||
dmap,
|
||||
key,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan keys in a distributed map, optionally matching a regex pattern
|
||||
*/
|
||||
async scan(dmap: string, match?: string): Promise<CacheScanResponse> {
|
||||
return this.httpClient.post<CacheScanResponse>("/v1/cache/scan", {
|
||||
dmap,
|
||||
match,
|
||||
});
|
||||
}
|
||||
}
|
||||
17
src/index.ts
17
src/index.ts
@ -3,6 +3,7 @@ import { AuthClient } from "./auth/client";
|
||||
import { DBClient } from "./db/client";
|
||||
import { PubSubClient } from "./pubsub/client";
|
||||
import { NetworkClient } from "./network/client";
|
||||
import { CacheClient } from "./cache/client";
|
||||
import { WSClientConfig } from "./core/ws";
|
||||
import {
|
||||
StorageAdapter,
|
||||
@ -23,6 +24,7 @@ export interface Client {
|
||||
db: DBClient;
|
||||
pubsub: PubSubClient;
|
||||
network: NetworkClient;
|
||||
cache: CacheClient;
|
||||
}
|
||||
|
||||
export function createClient(config: ClientConfig): Client {
|
||||
@ -52,16 +54,17 @@ export function createClient(config: ClientConfig): Client {
|
||||
wsURL,
|
||||
});
|
||||
const network = new NetworkClient(httpClient);
|
||||
const cache = new CacheClient(httpClient);
|
||||
|
||||
return {
|
||||
auth,
|
||||
db,
|
||||
pubsub,
|
||||
network,
|
||||
cache,
|
||||
};
|
||||
}
|
||||
|
||||
// Re-exports
|
||||
export { HttpClient } from "./core/http";
|
||||
export { WSClient } from "./core/ws";
|
||||
export { AuthClient } from "./auth/client";
|
||||
@ -70,6 +73,7 @@ export { QueryBuilder } from "./db/qb";
|
||||
export { Repository } from "./db/repository";
|
||||
export { PubSubClient, Subscription } from "./pubsub/client";
|
||||
export { NetworkClient } from "./network/client";
|
||||
export { CacheClient } from "./cache/client";
|
||||
export { SDKError } from "./errors";
|
||||
export { MemoryStorage, LocalStorageAdapter } from "./auth/types";
|
||||
export type { StorageAdapter, AuthConfig, WhoAmI } from "./auth/types";
|
||||
@ -86,3 +90,14 @@ export type {
|
||||
ProxyRequest,
|
||||
ProxyResponse,
|
||||
} from "./network/client";
|
||||
export type {
|
||||
CacheGetRequest,
|
||||
CacheGetResponse,
|
||||
CachePutRequest,
|
||||
CachePutResponse,
|
||||
CacheDeleteRequest,
|
||||
CacheDeleteResponse,
|
||||
CacheScanRequest,
|
||||
CacheScanResponse,
|
||||
CacheHealthResponse,
|
||||
} from "./cache/client";
|
||||
|
||||
169
tests/e2e/cache.test.ts
Normal file
169
tests/e2e/cache.test.ts
Normal file
@ -0,0 +1,169 @@
|
||||
import { describe, it, expect, beforeEach } from "vitest";
|
||||
import { createTestClient, skipIfNoGateway } from "./setup";
|
||||
|
||||
describe("Cache", () => {
|
||||
if (skipIfNoGateway()) {
|
||||
console.log("Skipping cache tests - gateway not available");
|
||||
return;
|
||||
}
|
||||
|
||||
const testDMap = "test-cache";
|
||||
|
||||
beforeEach(async () => {
|
||||
// Clean up test keys before each test
|
||||
const client = await createTestClient();
|
||||
try {
|
||||
const keys = await client.cache.scan(testDMap);
|
||||
for (const key of keys.keys) {
|
||||
await client.cache.delete(testDMap, key);
|
||||
}
|
||||
} catch (err) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
});
|
||||
|
||||
it("should check cache health", async () => {
|
||||
const client = await createTestClient();
|
||||
const health = await client.cache.health();
|
||||
expect(health.status).toBe("ok");
|
||||
expect(health.service).toBe("olric");
|
||||
});
|
||||
|
||||
it("should put and get a value", async () => {
|
||||
const client = await createTestClient();
|
||||
const testKey = "test-key-1";
|
||||
const testValue = "test-value-1";
|
||||
|
||||
// Put value
|
||||
const putResult = await client.cache.put(testDMap, testKey, testValue);
|
||||
expect(putResult.status).toBe("ok");
|
||||
expect(putResult.key).toBe(testKey);
|
||||
expect(putResult.dmap).toBe(testDMap);
|
||||
|
||||
// Get value
|
||||
const getResult = await client.cache.get(testDMap, testKey);
|
||||
expect(getResult.key).toBe(testKey);
|
||||
expect(getResult.value).toBe(testValue);
|
||||
expect(getResult.dmap).toBe(testDMap);
|
||||
});
|
||||
|
||||
it("should put and get complex objects", async () => {
|
||||
const client = await createTestClient();
|
||||
const testKey = "test-key-2";
|
||||
const testValue = {
|
||||
name: "John",
|
||||
age: 30,
|
||||
tags: ["developer", "golang"],
|
||||
};
|
||||
|
||||
// Put object
|
||||
await client.cache.put(testDMap, testKey, testValue);
|
||||
|
||||
// Get object
|
||||
const getResult = await client.cache.get(testDMap, testKey);
|
||||
expect(getResult.value).toBeDefined();
|
||||
expect(getResult.value.name).toBe(testValue.name);
|
||||
expect(getResult.value.age).toBe(testValue.age);
|
||||
});
|
||||
|
||||
it("should put value with TTL", async () => {
|
||||
const client = await createTestClient();
|
||||
const testKey = "test-key-ttl";
|
||||
const testValue = "ttl-value";
|
||||
|
||||
// Put with TTL
|
||||
const putResult = await client.cache.put(
|
||||
testDMap,
|
||||
testKey,
|
||||
testValue,
|
||||
"5m"
|
||||
);
|
||||
expect(putResult.status).toBe("ok");
|
||||
|
||||
// Verify value exists
|
||||
const getResult = await client.cache.get(testDMap, testKey);
|
||||
expect(getResult.value).toBe(testValue);
|
||||
});
|
||||
|
||||
it("should delete a value", async () => {
|
||||
const client = await createTestClient();
|
||||
const testKey = "test-key-delete";
|
||||
const testValue = "delete-me";
|
||||
|
||||
// Put value
|
||||
await client.cache.put(testDMap, testKey, testValue);
|
||||
|
||||
// Verify it exists
|
||||
const before = await client.cache.get(testDMap, testKey);
|
||||
expect(before.value).toBe(testValue);
|
||||
|
||||
// Delete value
|
||||
const deleteResult = await client.cache.delete(testDMap, testKey);
|
||||
expect(deleteResult.status).toBe("ok");
|
||||
expect(deleteResult.key).toBe(testKey);
|
||||
|
||||
// Verify it's deleted
|
||||
try {
|
||||
await client.cache.get(testDMap, testKey);
|
||||
expect.fail("Expected get to fail after delete");
|
||||
} catch (err: any) {
|
||||
expect(err.message).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
it("should scan keys", async () => {
|
||||
const client = await createTestClient();
|
||||
|
||||
// Put multiple keys
|
||||
await client.cache.put(testDMap, "key-1", "value-1");
|
||||
await client.cache.put(testDMap, "key-2", "value-2");
|
||||
await client.cache.put(testDMap, "key-3", "value-3");
|
||||
|
||||
// Scan all keys
|
||||
const scanResult = await client.cache.scan(testDMap);
|
||||
expect(scanResult.count).toBeGreaterThanOrEqual(3);
|
||||
expect(scanResult.keys).toContain("key-1");
|
||||
expect(scanResult.keys).toContain("key-2");
|
||||
expect(scanResult.keys).toContain("key-3");
|
||||
expect(scanResult.dmap).toBe(testDMap);
|
||||
});
|
||||
|
||||
it("should scan keys with regex match", async () => {
|
||||
const client = await createTestClient();
|
||||
|
||||
// Put keys with different patterns
|
||||
await client.cache.put(testDMap, "user-1", "value-1");
|
||||
await client.cache.put(testDMap, "user-2", "value-2");
|
||||
await client.cache.put(testDMap, "session-1", "value-3");
|
||||
|
||||
// Scan with regex match
|
||||
const scanResult = await client.cache.scan(testDMap, "^user-");
|
||||
expect(scanResult.count).toBeGreaterThanOrEqual(2);
|
||||
expect(scanResult.keys).toContain("user-1");
|
||||
expect(scanResult.keys).toContain("user-2");
|
||||
expect(scanResult.keys).not.toContain("session-1");
|
||||
});
|
||||
|
||||
it("should handle non-existent key gracefully", async () => {
|
||||
const client = await createTestClient();
|
||||
const nonExistentKey = "non-existent-key";
|
||||
|
||||
try {
|
||||
await client.cache.get(testDMap, nonExistentKey);
|
||||
expect.fail("Expected get to fail for non-existent key");
|
||||
} catch (err: any) {
|
||||
expect(err.message).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
it("should handle empty dmap name", async () => {
|
||||
const client = await createTestClient();
|
||||
|
||||
try {
|
||||
await client.cache.get("", "test-key");
|
||||
expect.fail("Expected get to fail with empty dmap");
|
||||
} catch (err: any) {
|
||||
expect(err.message).toBeDefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user