1
+ import type { CommandReply } from '../../commands/generic-transformers' ;
2
+ import type { CommandPolicies } from './policies-constants' ;
3
+ import { REQUEST_POLICIES_WITH_DEFAULTS , RESPONSE_POLICIES_WITH_DEFAULTS } from './policies-constants' ;
4
+ import type { PolicyResolver } from './types' ;
5
+ import { StaticPolicyResolver } from './static-policy-resolver' ;
6
+ import type { ModulePolicyRecords } from './static-policies-data' ;
7
+
8
+ /**
9
+ * Function type that returns command information from Redis
10
+ */
11
+ export type CommandFetcher = ( ) => Promise < Array < CommandReply > > ;
12
+
13
+ /**
14
+ * A factory for creating policy resolvers that dynamically build policies based on the Redis server's COMMAND response.
15
+ *
16
+ * This factory fetches command information from Redis and analyzes the response to determine
17
+ * appropriate routing policies for each command, returning a StaticPolicyResolver with the built policies.
18
+ */
19
+ export class DynamicPolicyResolverFactory {
20
+ /**
21
+ * Creates a StaticPolicyResolver by fetching command information from Redis
22
+ * and building appropriate policies based on the command characteristics.
23
+ *
24
+ * @param commandFetcher Function to fetch command information from Redis
25
+ * @param fallbackResolver Optional fallback resolver to use when policies are not found
26
+ * @returns A new StaticPolicyResolver with the fetched policies
27
+ */
28
+ static async create (
29
+ commandFetcher : CommandFetcher ,
30
+ fallbackResolver ?: PolicyResolver
31
+ ) : Promise < StaticPolicyResolver > {
32
+ const commands = await commandFetcher ( ) ;
33
+ const policies : ModulePolicyRecords = { } ;
34
+
35
+ for ( const command of commands ) {
36
+ const parsed = DynamicPolicyResolverFactory . #parseCommandName( command . name ) ;
37
+
38
+ // Skip commands with invalid format (more than one dot)
39
+ if ( ! parsed ) {
40
+ continue ;
41
+ }
42
+
43
+ const { moduleName, commandName } = parsed ;
44
+
45
+ // Initialize module if it doesn't exist
46
+ if ( ! policies [ moduleName ] ) {
47
+ policies [ moduleName ] = { } ;
48
+ }
49
+
50
+ // Determine policies for this command
51
+ const commandPolicies = DynamicPolicyResolverFactory . #buildCommandPolicies( command ) ;
52
+ policies [ moduleName ] [ commandName ] = commandPolicies ;
53
+ }
54
+
55
+ return new StaticPolicyResolver ( policies , fallbackResolver ) ;
56
+ }
57
+
58
+ /**
59
+ * Parses a command name to extract module and command components.
60
+ *
61
+ * Redis commands can be in format:
62
+ * - "ping" -> module: "std", command: "ping"
63
+ * - "ft.search" -> module: "ft", command: "search"
64
+ *
65
+ * Commands with more than one dot are invalid.
66
+ */
67
+ static #parseCommandName( fullCommandName : string ) : { moduleName : string ; commandName : string } | null {
68
+ const parts = fullCommandName . split ( '.' ) ;
69
+
70
+ if ( parts . length === 1 ) {
71
+ return { moduleName : 'std' , commandName : fullCommandName } ;
72
+ }
73
+
74
+ if ( parts . length === 2 ) {
75
+ return { moduleName : parts [ 0 ] , commandName : parts [ 1 ] } ;
76
+ }
77
+
78
+ // Commands with more than one dot are invalid in Redis
79
+ return null ;
80
+ }
81
+
82
+ /**
83
+ * Builds CommandPolicies for a command based on its characteristics.
84
+ *
85
+ * Priority order:
86
+ * 1. Use explicit policies from the command if available
87
+ * 2. Classify as DEFAULT_KEYLESS if keySpecification is empty
88
+ * 3. Classify as DEFAULT_KEYED if keySpecification is not empty
89
+ */
90
+ static #buildCommandPolicies( command : CommandReply ) : CommandPolicies {
91
+ // Determine if command is keyless based on keySpecification
92
+ const isKeyless = command . keySpecifications === 'keyless' ;
93
+
94
+ // Determine default policies based on key specification
95
+ const defaultRequest = isKeyless
96
+ ? REQUEST_POLICIES_WITH_DEFAULTS . DEFAULT_KEYLESS
97
+ : REQUEST_POLICIES_WITH_DEFAULTS . DEFAULT_KEYED ;
98
+ const defaultResponse = isKeyless
99
+ ? RESPONSE_POLICIES_WITH_DEFAULTS . DEFAULT_KEYLESS
100
+ : RESPONSE_POLICIES_WITH_DEFAULTS . DEFAULT_KEYED ;
101
+
102
+ return {
103
+ // request: command.policies.request ?? defaultRequest,
104
+ // response: command.policies.response ?? defaultResponse
105
+ request : defaultRequest ,
106
+ response : defaultResponse
107
+ } ;
108
+ }
109
+ }
0 commit comments