@@ -88,20 +88,60 @@ def __init__(self):
88
88
"appmetadata-{}-{}" .format (environment or "" , client_id or "" ),
89
89
}
90
90
91
- def find (self , credential_type , target = None , query = None ):
92
- target = target or []
91
+ def _get_access_token (
92
+ self ,
93
+ home_account_id , environment , client_id , realm , target , # Together they form a compound key
94
+ default = None ,
95
+ ): # O(1)
96
+ return self ._get (
97
+ self .CredentialType .ACCESS_TOKEN ,
98
+ self .key_makers [TokenCache .CredentialType .ACCESS_TOKEN ](
99
+ home_account_id = home_account_id ,
100
+ environment = environment ,
101
+ client_id = client_id ,
102
+ realm = realm ,
103
+ target = " " .join (target ),
104
+ ),
105
+ default = default )
106
+
107
+ def _get (self , credential_type , key , default = None ): # O(1)
108
+ with self ._lock :
109
+ return self ._cache .get (credential_type , {}).get (key , default )
110
+
111
+ def _find (self , credential_type , target = None , query = None ): # O(n) generator
112
+ """Returns a generator of matching entries.
113
+
114
+ It is O(1) for AT hits, and O(n) for other types.
115
+ Note that it holds a lock during the entire search.
116
+ """
117
+ target = sorted (target or []) # Match the order sorted by add()
93
118
assert isinstance (target , list ), "Invalid parameter type"
119
+
120
+ preferred_result = None
121
+ if (credential_type == self .CredentialType .ACCESS_TOKEN
122
+ and "home_account_id" in query and "environment" in query
123
+ and "client_id" in query and "realm" in query and target
124
+ ): # Special case for O(1) AT lookup
125
+ preferred_result = self ._get_access_token (
126
+ query ["home_account_id" ], query ["environment" ],
127
+ query ["client_id" ], query ["realm" ], target )
128
+ if preferred_result :
129
+ yield preferred_result
130
+
94
131
target_set = set (target )
95
132
with self ._lock :
96
133
# Since the target inside token cache key is (per schema) unsorted,
97
134
# there is no point to attempt an O(1) key-value search here.
98
135
# So we always do an O(n) in-memory search.
99
- return [entry
100
- for entry in self ._cache .get (credential_type , {}).values ()
101
- if is_subdict_of (query or {}, entry )
102
- and (target_set <= set (entry .get ("target" , "" ).split ())
103
- if target else True )
104
- ]
136
+ for entry in self ._cache .get (credential_type , {}).values ():
137
+ if is_subdict_of (query or {}, entry ) and (
138
+ target_set <= set (entry .get ("target" , "" ).split ())
139
+ if target else True ):
140
+ if entry != preferred_result : # Avoid yielding the same entry twice
141
+ yield entry
142
+
143
+ def find (self , credential_type , target = None , query = None ): # Obsolete. Use _find() instead.
144
+ return list (self ._find (credential_type , target = target , query = query ))
105
145
106
146
def add (self , event , now = None ):
107
147
"""Handle a token obtaining event, and add tokens into cache."""
0 commit comments