@reactionary/source 0.0.30 → 0.0.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +11 -0
- package/core/src/cache/cache-evaluation.interface.ts +19 -0
- package/core/src/cache/cache.interface.ts +38 -0
- package/core/src/cache/noop-cache.ts +42 -0
- package/core/src/cache/redis-cache.ts +55 -22
- package/core/src/client/client.ts +27 -3
- package/core/src/index.ts +3 -1
- package/core/src/providers/analytics.provider.ts +24 -1
- package/core/src/providers/base.provider.ts +84 -6
- package/core/src/providers/cart.provider.ts +18 -1
- package/core/src/providers/identity.provider.ts +18 -1
- package/core/src/providers/inventory.provider.ts +15 -2
- package/core/src/providers/price.provider.ts +18 -1
- package/core/src/providers/product.provider.ts +24 -1
- package/core/src/providers/search.provider.ts +18 -1
- package/core/src/schemas/mutations/product.mutation.ts +0 -1
- package/examples/node/src/providers/custom-algolia-product.provider.ts +2 -2
- package/examples/trpc-node/src/router-instance.ts +4 -2
- package/package.json +1 -1
- package/providers/algolia/src/core/initialize.ts +11 -9
- package/providers/algolia/src/providers/product.provider.ts +2 -2
- package/providers/algolia/src/providers/search.provider.ts +2 -2
- package/providers/commercetools/src/core/initialize.ts +27 -22
- package/providers/commercetools/src/providers/cart.provider.ts +3 -2
- package/providers/commercetools/src/providers/identity.provider.ts +3 -2
- package/providers/commercetools/src/providers/inventory.provider.ts +5 -14
- package/providers/commercetools/src/providers/price.provider.ts +3 -3
- package/providers/commercetools/src/providers/product.provider.ts +2 -2
- package/providers/commercetools/src/providers/search.provider.ts +2 -2
- package/providers/fake/src/core/initialize.ts +14 -12
- package/providers/fake/src/providers/identity.provider.ts +3 -2
- package/providers/fake/src/providers/product.provider.ts +2 -2
- package/providers/fake/src/providers/search.provider.ts +2 -2
- package/providers/posthog/src/core/initialize.ts +6 -4
- package/core/src/cache/caching-strategy.ts +0 -25
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Verification commands
|
|
2
|
+
- `npx nx affected:build` builds the projects affected by current changes.
|
|
3
|
+
- `npx nx affected:lint` lints and verifies the projects affected by current changes.
|
|
4
|
+
|
|
5
|
+
# Code style
|
|
6
|
+
- Use ES modules (import/export) syntax, not CommonJS (require).
|
|
7
|
+
- Keep things strongly typed. Do not use `as any` or similar unchecked type casts.
|
|
8
|
+
|
|
9
|
+
# Workflow
|
|
10
|
+
- Be sure to ensure that changes lint and build.
|
|
11
|
+
- Avoid introducing changes that would be breaking for any client consumer of the library without asking for clearance.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache evaluation result that determines how and if a query should be cached
|
|
3
|
+
*/
|
|
4
|
+
export interface CacheEvaluation {
|
|
5
|
+
/**
|
|
6
|
+
* The cache key to use for storing/retrieving the value
|
|
7
|
+
*/
|
|
8
|
+
key: string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* How long to cache the value in seconds
|
|
12
|
+
*/
|
|
13
|
+
cacheDurationInSeconds: number;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Whether this query result can be cached
|
|
17
|
+
*/
|
|
18
|
+
canCache: boolean;
|
|
19
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generic cache interface that can be implemented by different cache backends
|
|
5
|
+
* (Redis, memory, file-based, etc.)
|
|
6
|
+
*/
|
|
7
|
+
export interface Cache {
|
|
8
|
+
/**
|
|
9
|
+
* Retrieves a value from cache and validates it against the provided schema
|
|
10
|
+
*/
|
|
11
|
+
get<T>(key: string, schema: z.ZodType<T>): Promise<T | null>;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Stores a value in cache with optional expiration time
|
|
15
|
+
*/
|
|
16
|
+
put(key: string, value: unknown, ttlSeconds?: number): Promise<void>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Removes one or more keys from cache
|
|
20
|
+
* Supports wildcard patterns (implementation dependent)
|
|
21
|
+
*/
|
|
22
|
+
del(keys: string | string[]): Promise<void>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Finds all keys matching a pattern (implementation dependent)
|
|
26
|
+
*/
|
|
27
|
+
keys(pattern: string): Promise<string[]>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Clears all cache entries or entries matching a pattern
|
|
31
|
+
*/
|
|
32
|
+
clear(pattern?: string): Promise<void>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Gets basic cache statistics (implementation dependent)
|
|
36
|
+
*/
|
|
37
|
+
getStats(): Promise<{ hits: number; misses: number; size: number }>;
|
|
38
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Cache } from './cache.interface';
|
|
2
|
+
import z from 'zod';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* No-op cache implementation that never stores or returns data.
|
|
6
|
+
* Useful for testing or when caching should be disabled.
|
|
7
|
+
*/
|
|
8
|
+
export class NoOpCache implements Cache {
|
|
9
|
+
public async get<T>(_key: string, _schema: z.ZodType<T>): Promise<T | null> {
|
|
10
|
+
// Always return null - never a cache hit
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public async put(_key: string, _value: unknown, _ttlSeconds?: number): Promise<void> {
|
|
15
|
+
// No-op - silently ignore cache write requests
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public async del(_keys: string | string[]): Promise<void> {
|
|
20
|
+
// No-op - silently ignore delete requests
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public async keys(_pattern: string): Promise<string[]> {
|
|
25
|
+
// Always return empty array
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public async clear(_pattern?: string): Promise<void> {
|
|
30
|
+
// No-op - silently ignore clear requests
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public async getStats(): Promise<{ hits: number; misses: number; size: number }> {
|
|
35
|
+
// Always return zeros
|
|
36
|
+
return {
|
|
37
|
+
hits: 0,
|
|
38
|
+
misses: 0,
|
|
39
|
+
size: 0
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -1,41 +1,74 @@
|
|
|
1
1
|
import { Redis } from '@upstash/redis';
|
|
2
|
-
import {
|
|
3
|
-
import { BaseQuery } from '../schemas/queries/base.query';
|
|
4
|
-
import { Session } from '../schemas/session.schema';
|
|
5
|
-
import { BaseModel } from '../schemas/models/base.model';
|
|
2
|
+
import { Cache } from './cache.interface';
|
|
6
3
|
import z from 'zod';
|
|
7
4
|
|
|
8
|
-
export class RedisCache {
|
|
9
|
-
protected strategy: CachingStrategy;
|
|
5
|
+
export class RedisCache implements Cache {
|
|
10
6
|
protected redis: Redis;
|
|
11
7
|
|
|
12
|
-
constructor(
|
|
13
|
-
this.strategy = strategy;
|
|
8
|
+
constructor() {
|
|
14
9
|
this.redis = Redis.fromEnv();
|
|
15
10
|
}
|
|
16
11
|
|
|
17
|
-
public async get<T
|
|
18
|
-
|
|
12
|
+
public async get<T>(key: string, schema: z.ZodType<T>): Promise<T | null> {
|
|
13
|
+
if (!key) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
19
16
|
|
|
20
|
-
const
|
|
17
|
+
const unvalidated = await this.redis.get(key);
|
|
18
|
+
const parsed = schema.safeParse(unvalidated);
|
|
19
|
+
|
|
20
|
+
if (parsed.success) {
|
|
21
|
+
return parsed.data;
|
|
22
|
+
}
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const parsed = schema.safeParse(unvalidated);
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
public async put(key: string, value: unknown, ttlSeconds?: number): Promise<void> {
|
|
28
|
+
if (!key) {
|
|
29
|
+
return;
|
|
29
30
|
}
|
|
31
|
+
|
|
32
|
+
const options = ttlSeconds ? { ex: ttlSeconds } : undefined;
|
|
33
|
+
await this.redis.set(key, value, options);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public async del(keys: string | string[]): Promise<void> {
|
|
37
|
+
const keyArray = Array.isArray(keys) ? keys : [keys];
|
|
30
38
|
|
|
31
|
-
|
|
39
|
+
for (const key of keyArray) {
|
|
40
|
+
if (key.includes('*')) {
|
|
41
|
+
// Handle wildcard patterns
|
|
42
|
+
const matchingKeys = await this.redis.keys(key);
|
|
43
|
+
if (matchingKeys.length > 0) {
|
|
44
|
+
await this.redis.del(...matchingKeys);
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
// Delete specific key
|
|
48
|
+
await this.redis.del(key);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
32
51
|
}
|
|
33
52
|
|
|
34
|
-
public
|
|
35
|
-
|
|
53
|
+
public async keys(pattern: string): Promise<string[]> {
|
|
54
|
+
return await this.redis.keys(pattern);
|
|
55
|
+
}
|
|
36
56
|
|
|
37
|
-
|
|
38
|
-
|
|
57
|
+
public async clear(pattern?: string): Promise<void> {
|
|
58
|
+
const searchPattern = pattern || '*';
|
|
59
|
+
const keys = await this.redis.keys(searchPattern);
|
|
60
|
+
if (keys.length > 0) {
|
|
61
|
+
await this.redis.del(...keys);
|
|
39
62
|
}
|
|
40
63
|
}
|
|
64
|
+
|
|
65
|
+
public async getStats(): Promise<{ hits: number; misses: number; size: number }> {
|
|
66
|
+
// Basic cache statistics (could be enhanced with actual Redis metrics)
|
|
67
|
+
const keys = await this.redis.keys('*');
|
|
68
|
+
return {
|
|
69
|
+
hits: 0, // Would need to track this separately
|
|
70
|
+
misses: 0, // Would need to track this separately
|
|
71
|
+
size: keys.length
|
|
72
|
+
};
|
|
73
|
+
}
|
|
41
74
|
}
|
|
@@ -5,6 +5,8 @@ import { IdentityProvider } from '../providers/identity.provider';
|
|
|
5
5
|
import { CartProvider } from "../providers/cart.provider";
|
|
6
6
|
import { PriceProvider } from "../providers/price.provider";
|
|
7
7
|
import { InventoryProvider } from "../providers/inventory.provider";
|
|
8
|
+
import { Cache } from "../cache/cache.interface";
|
|
9
|
+
import { RedisCache } from "../cache/redis-cache";
|
|
8
10
|
|
|
9
11
|
export interface Client {
|
|
10
12
|
product: ProductProvider,
|
|
@@ -17,12 +19,23 @@ export interface Client {
|
|
|
17
19
|
inventory: InventoryProvider
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
export
|
|
22
|
+
export interface BuildClientOptions {
|
|
23
|
+
cache?: Cache;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function buildClient<T extends Partial<Client>>(
|
|
27
|
+
providerFactories: Array<(cache: Cache) => T>,
|
|
28
|
+
options: BuildClientOptions = {}
|
|
29
|
+
): Required<T> {
|
|
21
30
|
let client = { } as Required<T>;
|
|
22
31
|
|
|
32
|
+
// Create shared cache instance
|
|
33
|
+
const sharedCache = options.cache || new RedisCache();
|
|
34
|
+
|
|
23
35
|
const mergedAnalytics = [];
|
|
24
36
|
|
|
25
|
-
for (const
|
|
37
|
+
for (const factory of providerFactories) {
|
|
38
|
+
const provider = factory(sharedCache);
|
|
26
39
|
client = {
|
|
27
40
|
...client,
|
|
28
41
|
...provider
|
|
@@ -34,6 +47,17 @@ export function buildClient<T extends Partial<Client>>(providers: Array<T>): Req
|
|
|
34
47
|
}
|
|
35
48
|
|
|
36
49
|
client.analytics = mergedAnalytics;
|
|
50
|
+
|
|
51
|
+
// Add cache to complete the client
|
|
52
|
+
const completeClient = {
|
|
53
|
+
...client,
|
|
54
|
+
cache: sharedCache
|
|
55
|
+
} as Required<T>;
|
|
56
|
+
|
|
57
|
+
return completeClient;
|
|
58
|
+
}
|
|
37
59
|
|
|
38
|
-
|
|
60
|
+
// Convenience function to create a shared cache instance
|
|
61
|
+
export function createCache(): Cache {
|
|
62
|
+
return new RedisCache();
|
|
39
63
|
}
|
package/core/src/index.ts
CHANGED
|
@@ -2,9 +2,32 @@ import { AnalyticsEvent } from '../schemas/models/analytics.model';
|
|
|
2
2
|
import { AnalyticsMutation } from '../schemas/mutations/analytics.mutation';
|
|
3
3
|
import { AnalyticsQuery } from '../schemas/queries/analytics.query';
|
|
4
4
|
import { BaseProvider } from './base.provider';
|
|
5
|
+
import { CacheEvaluation } from '../cache/cache-evaluation.interface';
|
|
6
|
+
import { Session } from '../schemas/session.schema';
|
|
7
|
+
import * as crypto from 'crypto';
|
|
5
8
|
|
|
6
9
|
export abstract class AnalyticsProvider<
|
|
7
10
|
T extends AnalyticsEvent = AnalyticsEvent,
|
|
8
11
|
Q extends AnalyticsQuery = AnalyticsQuery,
|
|
9
12
|
M extends AnalyticsMutation = AnalyticsMutation
|
|
10
|
-
> extends BaseProvider<T, Q, M> {
|
|
13
|
+
> extends BaseProvider<T, Q, M> {
|
|
14
|
+
|
|
15
|
+
protected override getCacheEvaluation(query: Q, _session: Session): CacheEvaluation {
|
|
16
|
+
const providerName = this.constructor.name.toLowerCase();
|
|
17
|
+
|
|
18
|
+
// Hash analytics query parameters
|
|
19
|
+
const relevantFields = {
|
|
20
|
+
type: typeof query === 'object' && query !== null && 'type' in query ? (query as any).type : undefined,
|
|
21
|
+
dateRange: typeof query === 'object' && query !== null && 'dateRange' in query ? (query as any).dateRange : undefined,
|
|
22
|
+
filters: typeof query === 'object' && query !== null && 'filters' in query ? (query as any).filters : undefined
|
|
23
|
+
};
|
|
24
|
+
const analyticsHash = crypto.createHash('md5').update(JSON.stringify(relevantFields)).digest('hex').substring(0, 12);
|
|
25
|
+
const key = `${providerName}:analytics:${analyticsHash}`;
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
key,
|
|
29
|
+
cacheDurationInSeconds: 0,
|
|
30
|
+
canCache: false
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -4,6 +4,9 @@ import { BaseQuery } from '../schemas/queries/base.query';
|
|
|
4
4
|
import { BaseMutation } from '../schemas/mutations/base.mutation';
|
|
5
5
|
import { BaseModel } from '../schemas/models/base.model';
|
|
6
6
|
import { createProviderInstrumentation } from '@reactionary/otel';
|
|
7
|
+
import { Cache } from '../cache/cache.interface';
|
|
8
|
+
import { CacheEvaluation } from '../cache/cache-evaluation.interface';
|
|
9
|
+
import * as crypto from 'crypto';
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
12
|
* Base capability provider, responsible for mutations (changes) and queries (fetches)
|
|
@@ -14,10 +17,17 @@ export abstract class BaseProvider<
|
|
|
14
17
|
Q extends BaseQuery = BaseQuery,
|
|
15
18
|
M extends BaseMutation = BaseMutation
|
|
16
19
|
> {
|
|
17
|
-
|
|
20
|
+
protected instrumentation: ReturnType<typeof createProviderInstrumentation>;
|
|
21
|
+
protected cache: Cache;
|
|
18
22
|
|
|
19
|
-
constructor(
|
|
23
|
+
constructor(
|
|
24
|
+
public readonly schema: z.ZodType<T>,
|
|
25
|
+
public readonly querySchema: z.ZodType<Q, Q>,
|
|
26
|
+
public readonly mutationSchema: z.ZodType<M, M>,
|
|
27
|
+
cache: Cache
|
|
28
|
+
) {
|
|
20
29
|
this.instrumentation = createProviderInstrumentation(this.constructor.name);
|
|
30
|
+
this.cache = cache;
|
|
21
31
|
}
|
|
22
32
|
|
|
23
33
|
/**
|
|
@@ -46,13 +56,60 @@ export abstract class BaseProvider<
|
|
|
46
56
|
'query',
|
|
47
57
|
async (span) => {
|
|
48
58
|
span.setAttribute('provider.query.count', queries.length);
|
|
49
|
-
|
|
59
|
+
|
|
60
|
+
let cacheHits = 0;
|
|
61
|
+
let cacheMisses = 0;
|
|
62
|
+
const results: T[] = [];
|
|
50
63
|
|
|
51
|
-
|
|
52
|
-
|
|
64
|
+
// Process each query individually for cache efficiency
|
|
65
|
+
for (const query of queries) {
|
|
66
|
+
let result: T | null = null;
|
|
67
|
+
|
|
68
|
+
// Get cache evaluation from provider
|
|
69
|
+
const cacheInfo = this.getCacheEvaluation(query, session);
|
|
70
|
+
|
|
71
|
+
// Try cache first if caching is enabled
|
|
72
|
+
if (cacheInfo.canCache && cacheInfo.key) {
|
|
73
|
+
try {
|
|
74
|
+
result = await this.cache.get(cacheInfo.key, this.schema);
|
|
75
|
+
if (result) {
|
|
76
|
+
cacheHits++;
|
|
77
|
+
span.setAttribute('provider.cache.hit', true);
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
// Cache error shouldn't break the query - log and continue
|
|
81
|
+
console.warn(`Cache get error for ${this.constructor.name}:`, error);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// If not in cache, fetch from source
|
|
86
|
+
if (!result) {
|
|
87
|
+
const singleResult = await this.fetch([query], session);
|
|
88
|
+
result = singleResult[0];
|
|
89
|
+
cacheMisses++;
|
|
90
|
+
|
|
91
|
+
// Store in cache if caching is enabled
|
|
92
|
+
if (result && cacheInfo.canCache && cacheInfo.key) {
|
|
93
|
+
try {
|
|
94
|
+
await this.cache.put(cacheInfo.key, result, cacheInfo.cacheDurationInSeconds);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
// Cache put error shouldn't break the query - log and continue
|
|
97
|
+
console.warn(`Cache put error for ${this.constructor.name}:`, error);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (result) {
|
|
103
|
+
this.assert(result);
|
|
104
|
+
results.push(result);
|
|
105
|
+
}
|
|
53
106
|
}
|
|
54
107
|
|
|
55
108
|
span.setAttribute('provider.result.count', results.length);
|
|
109
|
+
span.setAttribute('provider.cache.hits', cacheHits);
|
|
110
|
+
span.setAttribute('provider.cache.misses', cacheMisses);
|
|
111
|
+
span.setAttribute('provider.cache.hit_ratio', cacheHits / (cacheHits + cacheMisses));
|
|
112
|
+
|
|
56
113
|
return results;
|
|
57
114
|
},
|
|
58
115
|
{ queryCount: queries.length }
|
|
@@ -68,8 +125,9 @@ export abstract class BaseProvider<
|
|
|
68
125
|
'mutate',
|
|
69
126
|
async (span) => {
|
|
70
127
|
span.setAttribute('provider.mutation.count', mutations.length);
|
|
128
|
+
|
|
129
|
+
// Perform the mutation
|
|
71
130
|
const result = await this.process(mutations, session);
|
|
72
|
-
|
|
73
131
|
this.assert(result);
|
|
74
132
|
|
|
75
133
|
return result;
|
|
@@ -92,4 +150,24 @@ export abstract class BaseProvider<
|
|
|
92
150
|
mutations: M[],
|
|
93
151
|
session: Session
|
|
94
152
|
): Promise<T>;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Provider-specific cache evaluation logic.
|
|
156
|
+
* Returns information about how this query should be cached.
|
|
157
|
+
* Override this method to enable caching with custom keys and TTL.
|
|
158
|
+
* Default implementation returns no caching.
|
|
159
|
+
*/
|
|
160
|
+
protected getCacheEvaluation(query: Q, session: Session): CacheEvaluation {
|
|
161
|
+
// Default implementation: generate a cache key but don't cache
|
|
162
|
+
const providerName = this.constructor.name.toLowerCase();
|
|
163
|
+
const userId = session.identity?.id || 'anonymous';
|
|
164
|
+
const queryHash = crypto.createHash('md5').update(JSON.stringify(query)).digest('hex').substring(0, 12);
|
|
165
|
+
const key = `${providerName}:${userId}:${queryHash}`;
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
key,
|
|
169
|
+
cacheDurationInSeconds: 0,
|
|
170
|
+
canCache: false
|
|
171
|
+
};
|
|
172
|
+
}
|
|
95
173
|
}
|
|
@@ -2,9 +2,26 @@ import { CartQuery } from "../schemas/queries/cart.query";
|
|
|
2
2
|
import { CartMutation } from "../schemas/mutations/cart.mutation";
|
|
3
3
|
import { BaseProvider } from "./base.provider";
|
|
4
4
|
import { Cart } from "../schemas/models/cart.model";
|
|
5
|
+
import { CacheEvaluation } from '../cache/cache-evaluation.interface';
|
|
6
|
+
import { Session } from '../schemas/session.schema';
|
|
7
|
+
import * as crypto from 'crypto';
|
|
5
8
|
|
|
6
9
|
export abstract class CartProvider<
|
|
7
10
|
T extends Cart = Cart,
|
|
8
11
|
Q extends CartQuery = CartQuery,
|
|
9
12
|
M extends CartMutation = CartMutation
|
|
10
|
-
> extends BaseProvider<T, Q, M> {
|
|
13
|
+
> extends BaseProvider<T, Q, M> {
|
|
14
|
+
|
|
15
|
+
protected override getCacheEvaluation(query: Q, session: Session): CacheEvaluation {
|
|
16
|
+
const providerName = this.constructor.name.toLowerCase();
|
|
17
|
+
const userId = session.identity?.id || 'anonymous';
|
|
18
|
+
const queryHash = crypto.createHash('md5').update(JSON.stringify(query)).digest('hex').substring(0, 12);
|
|
19
|
+
const key = `${providerName}:cart:${userId}:${queryHash}`;
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
key,
|
|
23
|
+
cacheDurationInSeconds: 0,
|
|
24
|
+
canCache: false
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -2,9 +2,26 @@ import { Identity } from "../schemas/models/identity.model";
|
|
|
2
2
|
import { IdentityQuery } from "../schemas/queries/identity.query";
|
|
3
3
|
import { IdentityMutation } from "../schemas/mutations/identity.mutation";
|
|
4
4
|
import { BaseProvider } from "./base.provider";
|
|
5
|
+
import { CacheEvaluation } from '../cache/cache-evaluation.interface';
|
|
6
|
+
import { Session } from '../schemas/session.schema';
|
|
5
7
|
|
|
6
8
|
export abstract class IdentityProvider<
|
|
7
9
|
T extends Identity = Identity,
|
|
8
10
|
Q extends IdentityQuery = IdentityQuery,
|
|
9
11
|
M extends IdentityMutation = IdentityMutation
|
|
10
|
-
> extends BaseProvider<T, Q, M> {
|
|
12
|
+
> extends BaseProvider<T, Q, M> {
|
|
13
|
+
|
|
14
|
+
protected override getCacheEvaluation(_query: Q, session: Session): CacheEvaluation {
|
|
15
|
+
const providerName = this.constructor.name.toLowerCase();
|
|
16
|
+
const userId = session.identity?.id;
|
|
17
|
+
const key = userId
|
|
18
|
+
? `${providerName}:identity:${userId}`
|
|
19
|
+
: `${providerName}:identity:anonymous`;
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
key,
|
|
23
|
+
cacheDurationInSeconds: 0,
|
|
24
|
+
canCache: false
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -1,11 +1,24 @@
|
|
|
1
|
-
import z from 'zod';
|
|
2
1
|
import { Inventory } from '../schemas/models/inventory.model';
|
|
3
2
|
import { InventoryQuery } from '../schemas/queries/inventory.query';
|
|
4
3
|
import { InventoryMutation } from '../schemas/mutations/inventory.mutation';
|
|
5
4
|
import { BaseProvider } from './base.provider';
|
|
5
|
+
import { CacheEvaluation } from '../cache/cache-evaluation.interface';
|
|
6
|
+
import { Session } from '../schemas/session.schema';
|
|
6
7
|
|
|
7
8
|
export abstract class InventoryProvider<
|
|
8
9
|
T extends Inventory = Inventory,
|
|
9
10
|
Q extends InventoryQuery = InventoryQuery,
|
|
10
11
|
M extends InventoryMutation = InventoryMutation
|
|
11
|
-
> extends BaseProvider<T, Q, M> {
|
|
12
|
+
> extends BaseProvider<T, Q, M> {
|
|
13
|
+
|
|
14
|
+
protected override getCacheEvaluation(query: Q, _session: Session): CacheEvaluation {
|
|
15
|
+
const providerName = this.constructor.name.toLowerCase();
|
|
16
|
+
const key = `${providerName}:inventory:${query.sku}`;
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
key,
|
|
20
|
+
cacheDurationInSeconds: 0,
|
|
21
|
+
canCache: false
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -2,9 +2,26 @@ import { Price } from '../schemas/models/price.model';
|
|
|
2
2
|
import { PriceMutation } from '../schemas/mutations/price.mutation';
|
|
3
3
|
import { PriceQuery } from '../schemas/queries/price.query';
|
|
4
4
|
import { BaseProvider } from './base.provider';
|
|
5
|
+
import { CacheEvaluation } from '../cache/cache-evaluation.interface';
|
|
6
|
+
import { Session } from '../schemas/session.schema';
|
|
7
|
+
import * as crypto from 'crypto';
|
|
5
8
|
|
|
6
9
|
export abstract class PriceProvider<
|
|
7
10
|
T extends Price = Price,
|
|
8
11
|
Q extends PriceQuery = PriceQuery,
|
|
9
12
|
M extends PriceMutation = PriceMutation
|
|
10
|
-
> extends BaseProvider<T, Q, M> {
|
|
13
|
+
> extends BaseProvider<T, Q, M> {
|
|
14
|
+
|
|
15
|
+
protected override getCacheEvaluation(query: Q, _session: Session): CacheEvaluation {
|
|
16
|
+
const providerName = this.constructor.name.toLowerCase();
|
|
17
|
+
// Hash complex SKU objects (price queries with currency, customer group, etc.)
|
|
18
|
+
const skuHash = crypto.createHash('md5').update(JSON.stringify(query.sku)).digest('hex').substring(0, 12);
|
|
19
|
+
const key = `${providerName}:price:${skuHash}`;
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
key,
|
|
23
|
+
cacheDurationInSeconds: 0,
|
|
24
|
+
canCache: false
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -1,11 +1,34 @@
|
|
|
1
1
|
import { Product } from '../schemas/models/product.model';
|
|
2
2
|
import { ProductMutation } from '../schemas/mutations/product.mutation';
|
|
3
|
-
import { ProductQuery } from '../schemas/queries/product.query';
|
|
3
|
+
import { ProductQuery, ProductQueryById, ProductQueryBySlug } from '../schemas/queries/product.query';
|
|
4
4
|
import { BaseProvider } from './base.provider';
|
|
5
|
+
import { Session } from '../schemas/session.schema';
|
|
6
|
+
import { CacheEvaluation } from '../cache/cache-evaluation.interface';
|
|
7
|
+
import * as crypto from 'crypto';
|
|
5
8
|
|
|
6
9
|
export abstract class ProductProvider<
|
|
7
10
|
T extends Product = Product,
|
|
8
11
|
Q extends ProductQuery = ProductQuery,
|
|
9
12
|
M extends ProductMutation = ProductMutation
|
|
10
13
|
> extends BaseProvider<T, Q, M> {
|
|
14
|
+
|
|
15
|
+
protected override getCacheEvaluation(query: Q, _session: Session): CacheEvaluation {
|
|
16
|
+
const providerName = this.constructor.name.toLowerCase();
|
|
17
|
+
|
|
18
|
+
let key: string;
|
|
19
|
+
if (query.query === 'slug') {
|
|
20
|
+
key = `${providerName}:product:slug:${(query as ProductQueryBySlug).slug}`;
|
|
21
|
+
} else if (query.query === 'id') {
|
|
22
|
+
key = `${providerName}:product:id:${(query as ProductQueryById).id}`;
|
|
23
|
+
} else {
|
|
24
|
+
const queryHash = crypto.createHash('md5').update(JSON.stringify(query)).digest('hex').substring(0, 12);
|
|
25
|
+
key = `${providerName}:product:${queryHash}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
key,
|
|
30
|
+
cacheDurationInSeconds: 300, // Products are moderately stable - 5 minutes
|
|
31
|
+
canCache: true
|
|
32
|
+
};
|
|
33
|
+
}
|
|
11
34
|
}
|
|
@@ -2,11 +2,28 @@ import { SearchResult } from '../schemas/models/search.model';
|
|
|
2
2
|
import { SearchQuery } from '../schemas/queries/search.query';
|
|
3
3
|
import { SearchMutation } from '../schemas/mutations/search.mutation';
|
|
4
4
|
import { BaseProvider } from './base.provider';
|
|
5
|
+
import { CacheEvaluation } from '../cache/cache-evaluation.interface';
|
|
6
|
+
import { Session } from '../schemas/session.schema';
|
|
7
|
+
import * as crypto from 'crypto';
|
|
5
8
|
|
|
6
9
|
export abstract class SearchProvider<
|
|
7
10
|
T extends SearchResult = SearchResult,
|
|
8
11
|
Q extends SearchQuery = SearchQuery,
|
|
9
12
|
M extends SearchMutation = SearchMutation
|
|
10
|
-
> extends BaseProvider<T, Q, M> {
|
|
13
|
+
> extends BaseProvider<T, Q, M> {
|
|
14
|
+
|
|
15
|
+
protected override getCacheEvaluation(query: Q, _session: Session): CacheEvaluation {
|
|
16
|
+
const providerName = this.constructor.name.toLowerCase();
|
|
17
|
+
// Hash search parameters (term, filters, pagination, etc.)
|
|
18
|
+
const searchHash = crypto.createHash('md5').update(JSON.stringify(query.search)).digest('hex').substring(0, 12);
|
|
19
|
+
const key = `${providerName}:search:${searchHash}`;
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
key,
|
|
23
|
+
cacheDurationInSeconds: 0,
|
|
24
|
+
canCache: false
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
11
28
|
|
|
12
29
|
|
|
@@ -3,8 +3,8 @@ import { CustomProduct, CustomProductSchema } from '../schemas/custom-product.sc
|
|
|
3
3
|
import { ProductMutationSchema, ProductQuerySchema } from '@reactionary/core';
|
|
4
4
|
|
|
5
5
|
export class CustomAlgoliaProductProvider extends AlgoliaProductProvider<CustomProduct> {
|
|
6
|
-
constructor(config: AlgoliaConfiguration) {
|
|
7
|
-
super(config, CustomProductSchema, ProductQuerySchema, ProductMutationSchema);
|
|
6
|
+
constructor(config: AlgoliaConfiguration, cache: any) {
|
|
7
|
+
super(config, CustomProductSchema, ProductQuerySchema, ProductMutationSchema, cache);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
public parse(data: any): CustomProduct {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildClient } from "@reactionary/core";
|
|
1
|
+
import { buildClient, NoOpCache } from "@reactionary/core";
|
|
2
2
|
import { withAlgoliaCapabilities } from "@reactionary/provider-algolia";
|
|
3
3
|
import { withCommercetoolsCapabilities } from "@reactionary/provider-commercetools";
|
|
4
4
|
import { withFakeCapabilities } from "@reactionary/provider-fake";
|
|
@@ -45,7 +45,9 @@ export const client = buildClient([
|
|
|
45
45
|
},
|
|
46
46
|
{ analytics: true }
|
|
47
47
|
),
|
|
48
|
-
]
|
|
48
|
+
], {
|
|
49
|
+
cache: new NoOpCache()
|
|
50
|
+
});
|
|
49
51
|
|
|
50
52
|
export const mergedRouter = createTRPCRouter(client);
|
|
51
53
|
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Client, ProductMutationSchema, ProductQuerySchema, ProductSchema, SearchMutationSchema, SearchQuerySchema } from "@reactionary/core";
|
|
1
|
+
import { Client, ProductMutationSchema, ProductQuerySchema, ProductSchema, SearchMutationSchema, SearchQuerySchema, Cache } from "@reactionary/core";
|
|
2
2
|
import { AlgoliaProductProvider } from "../providers/product.provider";
|
|
3
3
|
import { AlgoliaSearchProvider } from "../providers/search.provider";
|
|
4
4
|
import { AlgoliaCapabilities } from "../schema/capabilities.schema";
|
|
@@ -6,15 +6,17 @@ import { AlgoliaConfiguration } from "../schema/configuration.schema";
|
|
|
6
6
|
import { AlgoliaSearchResultSchema } from "../schema/search.schema";
|
|
7
7
|
|
|
8
8
|
export function withAlgoliaCapabilities(configuration: AlgoliaConfiguration, capabilities: AlgoliaCapabilities) {
|
|
9
|
-
|
|
9
|
+
return (cache: Cache) => {
|
|
10
|
+
const client: Partial<Client> = {};
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
if (capabilities.product) {
|
|
13
|
+
client.product = new AlgoliaProductProvider(configuration, ProductSchema, ProductQuerySchema, ProductMutationSchema, cache);
|
|
14
|
+
}
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
if (capabilities.search) {
|
|
17
|
+
client.search = new AlgoliaSearchProvider(configuration, AlgoliaSearchResultSchema, SearchQuerySchema, SearchMutationSchema, cache);
|
|
18
|
+
}
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
return client;
|
|
21
|
+
};
|
|
20
22
|
}
|
|
@@ -9,8 +9,8 @@ export class AlgoliaProductProvider<
|
|
|
9
9
|
> extends ProductProvider<T, Q, M> {
|
|
10
10
|
protected config: AlgoliaConfiguration;
|
|
11
11
|
|
|
12
|
-
constructor(config: AlgoliaConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M
|
|
13
|
-
super(schema, querySchema, mutationSchema);
|
|
12
|
+
constructor(config: AlgoliaConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>, cache: any) {
|
|
13
|
+
super(schema, querySchema, mutationSchema, cache);
|
|
14
14
|
|
|
15
15
|
this.config = config;
|
|
16
16
|
}
|
|
@@ -19,8 +19,8 @@ export class AlgoliaSearchProvider<
|
|
|
19
19
|
> extends SearchProvider<T, Q, M> {
|
|
20
20
|
protected config: AlgoliaConfiguration;
|
|
21
21
|
|
|
22
|
-
constructor(config: AlgoliaConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M
|
|
23
|
-
super(schema, querySchema, mutationSchema);
|
|
22
|
+
constructor(config: AlgoliaConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>, cache: any) {
|
|
23
|
+
super(schema, querySchema, mutationSchema, cache);
|
|
24
24
|
|
|
25
25
|
this.config = config;
|
|
26
26
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CartMutationSchema, CartQuerySchema, CartSchema, Client, IdentityMutationSchema, IdentityQuerySchema, IdentitySchema, InventoryQuerySchema, InventorySchema, PriceMutationSchema, PriceQuerySchema, PriceSchema, ProductMutationSchema, ProductQuerySchema, ProductSchema, SearchMutationSchema, SearchQuerySchema, SearchResultSchema } from "@reactionary/core";
|
|
1
|
+
import { CartMutationSchema, CartQuerySchema, CartSchema, Client, IdentityMutationSchema, IdentityQuerySchema, IdentitySchema, InventoryQuerySchema, InventorySchema, PriceMutationSchema, PriceQuerySchema, PriceSchema, ProductMutationSchema, ProductQuerySchema, ProductSchema, SearchMutationSchema, SearchQuerySchema, SearchResultSchema, Cache } from "@reactionary/core";
|
|
2
2
|
import { CommercetoolsCapabilities } from "../schema/capabilities.schema";
|
|
3
3
|
import { CommercetoolsSearchProvider } from "../providers/search.provider";
|
|
4
4
|
import { CommercetoolsProductProvider } from '../providers/product.provider';
|
|
@@ -9,32 +9,37 @@ import { CommercetoolsInventoryProvider } from "../providers/inventory.provider"
|
|
|
9
9
|
import { CommercetoolsPriceProvider } from "../providers/price.provider";
|
|
10
10
|
import { InventoryMutationSchema } from "core/src/schemas/mutations/inventory.mutation";
|
|
11
11
|
|
|
12
|
-
export function withCommercetoolsCapabilities(
|
|
13
|
-
|
|
12
|
+
export function withCommercetoolsCapabilities(
|
|
13
|
+
configuration: CommercetoolsConfiguration,
|
|
14
|
+
capabilities: CommercetoolsCapabilities
|
|
15
|
+
) {
|
|
16
|
+
return (cache: Cache) => {
|
|
17
|
+
const client: Partial<Client> = {};
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
if (capabilities.product) {
|
|
20
|
+
client.product = new CommercetoolsProductProvider(configuration, ProductSchema, ProductQuerySchema, ProductMutationSchema, cache);
|
|
21
|
+
}
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
if (capabilities.search) {
|
|
24
|
+
client.search = new CommercetoolsSearchProvider(configuration, SearchResultSchema, SearchQuerySchema, SearchMutationSchema, cache);
|
|
25
|
+
}
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
if (capabilities.identity) {
|
|
28
|
+
client.identity = new CommercetoolsIdentityProvider(configuration, IdentitySchema, IdentityQuerySchema, IdentityMutationSchema, cache);
|
|
29
|
+
}
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
if (capabilities.cart) {
|
|
32
|
+
client.cart = new CommercetoolsCartProvider(configuration, CartSchema, CartQuerySchema, CartMutationSchema, cache);
|
|
33
|
+
}
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
if (capabilities.inventory) {
|
|
36
|
+
client.inventory = new CommercetoolsInventoryProvider(configuration, InventorySchema, InventoryQuerySchema, InventoryMutationSchema, cache);
|
|
37
|
+
}
|
|
34
38
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
if (capabilities.price) {
|
|
40
|
+
client.price = new CommercetoolsPriceProvider(configuration, PriceSchema, PriceQuerySchema, PriceMutationSchema, cache);
|
|
41
|
+
}
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
return client;
|
|
44
|
+
};
|
|
40
45
|
}
|
|
@@ -25,9 +25,10 @@ export class CommercetoolsCartProvider<
|
|
|
25
25
|
config: CommercetoolsConfiguration,
|
|
26
26
|
schema: z.ZodType<T>,
|
|
27
27
|
querySchema: z.ZodType<Q, Q>,
|
|
28
|
-
mutationSchema: z.ZodType<M, M
|
|
28
|
+
mutationSchema: z.ZodType<M, M>,
|
|
29
|
+
cache: any
|
|
29
30
|
) {
|
|
30
|
-
super(schema, querySchema, mutationSchema);
|
|
31
|
+
super(schema, querySchema, mutationSchema, cache);
|
|
31
32
|
|
|
32
33
|
this.config = config;
|
|
33
34
|
}
|
|
@@ -21,9 +21,10 @@ export class CommercetoolsIdentityProvider<
|
|
|
21
21
|
config: CommercetoolsConfiguration,
|
|
22
22
|
schema: z.ZodType<T>,
|
|
23
23
|
querySchema: z.ZodType<Q, Q>,
|
|
24
|
-
mutationSchema: z.ZodType<M, M
|
|
24
|
+
mutationSchema: z.ZodType<M, M>,
|
|
25
|
+
cache: any
|
|
25
26
|
) {
|
|
26
|
-
super(schema, querySchema, mutationSchema);
|
|
27
|
+
super(schema, querySchema, mutationSchema, cache);
|
|
27
28
|
|
|
28
29
|
this.config = config;
|
|
29
30
|
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
BaseCachingStrategy,
|
|
3
2
|
Inventory,
|
|
4
3
|
InventoryProvider,
|
|
5
4
|
InventoryQuery,
|
|
6
|
-
RedisCache,
|
|
7
5
|
Session,
|
|
8
6
|
} from '@reactionary/core';
|
|
9
7
|
import z from 'zod';
|
|
@@ -17,32 +15,25 @@ export class CommercetoolsInventoryProvider<
|
|
|
17
15
|
M extends InventoryMutation = InventoryMutation
|
|
18
16
|
> extends InventoryProvider<T, Q, M> {
|
|
19
17
|
protected config: CommercetoolsConfiguration;
|
|
20
|
-
protected cache = new RedisCache(new BaseCachingStrategy());
|
|
21
18
|
|
|
22
19
|
constructor(
|
|
23
20
|
config: CommercetoolsConfiguration,
|
|
24
21
|
schema: z.ZodType<T>,
|
|
25
22
|
querySchema: z.ZodType<Q, Q>,
|
|
26
|
-
mutationSchema: z.ZodType<M, M
|
|
23
|
+
mutationSchema: z.ZodType<M, M>,
|
|
24
|
+
cache: any
|
|
27
25
|
) {
|
|
28
|
-
super(schema, querySchema, mutationSchema);
|
|
26
|
+
super(schema, querySchema, mutationSchema, cache);
|
|
29
27
|
|
|
30
28
|
this.config = config;
|
|
31
29
|
}
|
|
32
30
|
|
|
33
31
|
protected override async fetch(queries: Q[], session: Session): Promise<T[]> {
|
|
32
|
+
// Base class now handles caching automatically
|
|
34
33
|
const results = [];
|
|
35
34
|
|
|
36
35
|
for (const query of queries) {
|
|
37
|
-
|
|
38
|
-
console.log('from cache: ', result);
|
|
39
|
-
|
|
40
|
-
if (!result) {
|
|
41
|
-
result = await this.get(query, session);
|
|
42
|
-
|
|
43
|
-
this.cache.put(query, session, result);
|
|
44
|
-
}
|
|
45
|
-
|
|
36
|
+
const result = await this.get(query, session);
|
|
46
37
|
results.push(result);
|
|
47
38
|
}
|
|
48
39
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BaseMutation, Price, PriceProvider, PriceQuery, Session } from '@reactionary/core';
|
|
2
2
|
import z from 'zod';
|
|
3
3
|
import { CommercetoolsConfiguration } from '../schema/configuration.schema';
|
|
4
4
|
import { CommercetoolsClient } from '../core/client';
|
|
@@ -11,8 +11,8 @@ export class CommercetoolsPriceProvider<
|
|
|
11
11
|
> extends PriceProvider<T, Q, M> {
|
|
12
12
|
protected config: CommercetoolsConfiguration;
|
|
13
13
|
|
|
14
|
-
constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M
|
|
15
|
-
super(schema, querySchema, mutationSchema);
|
|
14
|
+
constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>, cache: any) {
|
|
15
|
+
super(schema, querySchema, mutationSchema, cache);
|
|
16
16
|
|
|
17
17
|
this.config = config;
|
|
18
18
|
}
|
|
@@ -18,8 +18,8 @@ export class CommercetoolsProductProvider<
|
|
|
18
18
|
> extends ProductProvider<T, Q, M> {
|
|
19
19
|
protected config: CommercetoolsConfiguration;
|
|
20
20
|
|
|
21
|
-
constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M
|
|
22
|
-
super(schema, querySchema, mutationSchema);
|
|
21
|
+
constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>, cache: any) {
|
|
22
|
+
super(schema, querySchema, mutationSchema, cache);
|
|
23
23
|
|
|
24
24
|
this.config = config;
|
|
25
25
|
}
|
|
@@ -18,8 +18,8 @@ export class CommercetoolsSearchProvider<
|
|
|
18
18
|
> extends SearchProvider<T, Q, M> {
|
|
19
19
|
protected config: CommercetoolsConfiguration;
|
|
20
20
|
|
|
21
|
-
constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M
|
|
22
|
-
super(schema, querySchema, mutationSchema);
|
|
21
|
+
constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>, cache: any) {
|
|
22
|
+
super(schema, querySchema, mutationSchema, cache);
|
|
23
23
|
|
|
24
24
|
this.config = config;
|
|
25
25
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Client, IdentityMutationSchema, IdentityQuerySchema, IdentitySchema, ProductMutationSchema, ProductQuerySchema, ProductSchema, SearchMutationSchema, SearchQuerySchema, SearchResultSchema } from "@reactionary/core";
|
|
1
|
+
import { Client, IdentityMutationSchema, IdentityQuerySchema, IdentitySchema, ProductMutationSchema, ProductQuerySchema, ProductSchema, SearchMutationSchema, SearchQuerySchema, SearchResultSchema, Cache } from "@reactionary/core";
|
|
2
2
|
import { FakeProductProvider } from "../providers/product.provider";
|
|
3
3
|
import { FakeSearchProvider } from "../providers/search.provider";
|
|
4
4
|
import { FakeConfiguration } from "../schema/configuration.schema";
|
|
@@ -6,19 +6,21 @@ import { FakeCapabilities } from "../schema/capabilities.schema";
|
|
|
6
6
|
import { FakeIdentityProvider } from "../providers/identity.provider";
|
|
7
7
|
|
|
8
8
|
export function withFakeCapabilities(configuration: FakeConfiguration, capabilities: FakeCapabilities) {
|
|
9
|
-
|
|
9
|
+
return (cache: Cache) => {
|
|
10
|
+
const client = {} as Partial<Client>;
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
if (capabilities.product) {
|
|
13
|
+
client.product = new FakeProductProvider(configuration, ProductSchema, ProductQuerySchema, ProductMutationSchema, cache);
|
|
14
|
+
}
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
if (capabilities.search) {
|
|
17
|
+
client.search = new FakeSearchProvider(configuration, SearchResultSchema, SearchQuerySchema, SearchMutationSchema, cache);
|
|
18
|
+
}
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
if (capabilities.identity) {
|
|
21
|
+
client.identity = new FakeIdentityProvider(configuration, IdentitySchema, IdentityQuerySchema, IdentityMutationSchema, cache);
|
|
22
|
+
}
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
return client;
|
|
25
|
+
};
|
|
24
26
|
}
|
|
@@ -21,9 +21,10 @@ export class FakeIdentityProvider<
|
|
|
21
21
|
config: FakeConfiguration,
|
|
22
22
|
schema: z.ZodType<T>,
|
|
23
23
|
querySchema: z.ZodType<Q, Q>,
|
|
24
|
-
mutationSchema: z.ZodType<M, M
|
|
24
|
+
mutationSchema: z.ZodType<M, M>,
|
|
25
|
+
cache: any
|
|
25
26
|
) {
|
|
26
|
-
super(schema, querySchema, mutationSchema);
|
|
27
|
+
super(schema, querySchema, mutationSchema, cache);
|
|
27
28
|
|
|
28
29
|
this.config = config;
|
|
29
30
|
}
|
|
@@ -17,8 +17,8 @@ export class FakeProductProvider<
|
|
|
17
17
|
> extends ProductProvider<T, Q, M> {
|
|
18
18
|
protected config: FakeConfiguration;
|
|
19
19
|
|
|
20
|
-
constructor(config: FakeConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M
|
|
21
|
-
super(schema, querySchema, mutationSchema);
|
|
20
|
+
constructor(config: FakeConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>, cache: any) {
|
|
21
|
+
super(schema, querySchema, mutationSchema, cache);
|
|
22
22
|
|
|
23
23
|
this.config = config;
|
|
24
24
|
}
|
|
@@ -20,8 +20,8 @@ export class FakeSearchProvider<
|
|
|
20
20
|
> extends SearchProvider<T, Q, M> {
|
|
21
21
|
protected config: FakeConfiguration;
|
|
22
22
|
|
|
23
|
-
constructor(config: FakeConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M
|
|
24
|
-
super(schema, querySchema, mutationSchema);
|
|
23
|
+
constructor(config: FakeConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>, cache: any) {
|
|
24
|
+
super(schema, querySchema, mutationSchema, cache);
|
|
25
25
|
|
|
26
26
|
this.config = config;
|
|
27
27
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { Client } from "@reactionary/core";
|
|
1
|
+
import { Client, Cache } from "@reactionary/core";
|
|
2
2
|
import { PosthogConfiguration } from "../schema/configuration.schema";
|
|
3
3
|
import { PosthogCapabilities } from "../schema/capabilities.schema";
|
|
4
4
|
|
|
5
|
-
export function withPosthogCapabilities(
|
|
6
|
-
|
|
5
|
+
export function withPosthogCapabilities(_configuration: PosthogConfiguration, _capabilities: PosthogCapabilities) {
|
|
6
|
+
return (_cache: Cache) => {
|
|
7
|
+
const client: Partial<Client> = {};
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
return client;
|
|
10
|
+
};
|
|
9
11
|
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { BaseQuery } from "../schemas/queries/base.query";
|
|
2
|
-
import { InventoryQuery } from "../schemas/queries/inventory.query";
|
|
3
|
-
import { Session } from "../schemas/session.schema";
|
|
4
|
-
|
|
5
|
-
export interface CachingStrategyEvaluation {
|
|
6
|
-
key: string;
|
|
7
|
-
cacheDurationInSeconds: number;
|
|
8
|
-
canCache: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface CachingStrategy {
|
|
12
|
-
get(query: BaseQuery, session: Session): CachingStrategyEvaluation;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class BaseCachingStrategy implements CachingStrategy {
|
|
16
|
-
public get(query: BaseQuery, session: Session): CachingStrategyEvaluation {
|
|
17
|
-
const q = query as InventoryQuery;
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
key: q.sku,
|
|
21
|
-
cacheDurationInSeconds: 300,
|
|
22
|
-
canCache: true
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|