mirror of
https://github.com/DeBrosOfficial/network-ts-sdk.git
synced 2025-12-12 18:28:50 +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",
|
"name": "@debros/network-ts-sdk",
|
||||||
"version": "0.2.5",
|
"version": "0.3.0",
|
||||||
"description": "TypeScript SDK for DeBros Network Gateway",
|
"description": "TypeScript SDK for DeBros Network Gateway",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"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 { DBClient } from "./db/client";
|
||||||
import { PubSubClient } from "./pubsub/client";
|
import { PubSubClient } from "./pubsub/client";
|
||||||
import { NetworkClient } from "./network/client";
|
import { NetworkClient } from "./network/client";
|
||||||
|
import { CacheClient } from "./cache/client";
|
||||||
import { WSClientConfig } from "./core/ws";
|
import { WSClientConfig } from "./core/ws";
|
||||||
import {
|
import {
|
||||||
StorageAdapter,
|
StorageAdapter,
|
||||||
@ -23,6 +24,7 @@ export interface Client {
|
|||||||
db: DBClient;
|
db: DBClient;
|
||||||
pubsub: PubSubClient;
|
pubsub: PubSubClient;
|
||||||
network: NetworkClient;
|
network: NetworkClient;
|
||||||
|
cache: CacheClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createClient(config: ClientConfig): Client {
|
export function createClient(config: ClientConfig): Client {
|
||||||
@ -52,16 +54,17 @@ export function createClient(config: ClientConfig): Client {
|
|||||||
wsURL,
|
wsURL,
|
||||||
});
|
});
|
||||||
const network = new NetworkClient(httpClient);
|
const network = new NetworkClient(httpClient);
|
||||||
|
const cache = new CacheClient(httpClient);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
auth,
|
auth,
|
||||||
db,
|
db,
|
||||||
pubsub,
|
pubsub,
|
||||||
network,
|
network,
|
||||||
|
cache,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-exports
|
|
||||||
export { HttpClient } from "./core/http";
|
export { HttpClient } from "./core/http";
|
||||||
export { WSClient } from "./core/ws";
|
export { WSClient } from "./core/ws";
|
||||||
export { AuthClient } from "./auth/client";
|
export { AuthClient } from "./auth/client";
|
||||||
@ -70,6 +73,7 @@ export { QueryBuilder } from "./db/qb";
|
|||||||
export { Repository } from "./db/repository";
|
export { Repository } from "./db/repository";
|
||||||
export { PubSubClient, Subscription } from "./pubsub/client";
|
export { PubSubClient, Subscription } from "./pubsub/client";
|
||||||
export { NetworkClient } from "./network/client";
|
export { NetworkClient } from "./network/client";
|
||||||
|
export { CacheClient } from "./cache/client";
|
||||||
export { SDKError } from "./errors";
|
export { SDKError } from "./errors";
|
||||||
export { MemoryStorage, LocalStorageAdapter } from "./auth/types";
|
export { MemoryStorage, LocalStorageAdapter } from "./auth/types";
|
||||||
export type { StorageAdapter, AuthConfig, WhoAmI } from "./auth/types";
|
export type { StorageAdapter, AuthConfig, WhoAmI } from "./auth/types";
|
||||||
@ -86,3 +90,14 @@ export type {
|
|||||||
ProxyRequest,
|
ProxyRequest,
|
||||||
ProxyResponse,
|
ProxyResponse,
|
||||||
} from "./network/client";
|
} 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