@@ -61,6 +61,7 @@ export interface BatchRequestOptions<Res> extends RequestOptions<Res> {
61
61
*/
62
62
export class OPAClient {
63
63
private opa : Opa ;
64
+ private opaFallback : boolean = false ;
64
65
65
66
/** Create a new `OPA` instance.
66
67
* @param serverURL - The OPA URL, e.g. `https://opa.internal.corp:8443/`.
@@ -167,59 +168,30 @@ export class OPAClient {
167
168
) ;
168
169
let res : BatchMixedResults | BatchSuccessfulPolicyEvaluation | undefined ;
169
170
170
- try {
171
- const resp = await this . opa . executeBatchPolicyWithInput (
172
- { path, requestBody : { inputs : inps } } ,
173
- opts ,
174
- ) ;
175
-
176
- res = resp . batchMixedResults || resp . batchSuccessfulPolicyEvaluation ;
177
- } catch ( err ) {
178
- // TODO(sr): memoize fallback
179
- if (
180
- err instanceof SDKError &&
181
- err . httpMeta . response . status == 404 &&
182
- opts ?. fallback
183
- ) {
184
- // run a sequence of evaluatePolicyWithInput() instead, via Promise.all/Promise.allSettled
185
- let items : [ string , ServerError | SuccessfulPolicyResponse ] [ ] ;
186
- const inputs = Object . values ( inps ) ;
187
- const keys = Object . keys ( inps ) ;
188
- const ps = inputs . map ( ( input ) =>
189
- this . opa
190
- . executePolicyWithInput ( { path, requestBody : { input } } )
191
- . then ( ( { successfulPolicyResponse : res } ) => res ) ,
171
+ if ( this . opaFallback && opts ?. fallback ) {
172
+ // memoized fallback: we have hit a 404 here before
173
+ const responses = await this . fallbackBatch ( path , inps , opts ) ;
174
+ res = { responses } ;
175
+ } else {
176
+ try {
177
+ const resp = await this . opa . executeBatchPolicyWithInput (
178
+ { path, requestBody : { inputs : inps } } ,
179
+ opts ,
192
180
) ;
193
- if ( opts ?. rejectMixed ) {
194
- items = await Promise . all ( ps ) . then ( ( results ) =>
195
- results . map ( ( result , i ) => {
196
- if ( ! result ) throw `no result in API response` ;
197
- return [
198
- keys [ i ] as string , // can't be undefined
199
- result ,
200
- ] ;
201
- } ) ,
202
- ) ;
181
+
182
+ res = resp . batchMixedResults || resp . batchSuccessfulPolicyEvaluation ;
183
+ } catch ( err ) {
184
+ if (
185
+ err instanceof SDKError &&
186
+ err . httpMeta . response . status == 404 &&
187
+ opts ?. fallback
188
+ ) {
189
+ this . opaFallback = true ;
190
+ const responses = await this . fallbackBatch ( path , inps , opts ) ;
191
+ res = { responses } ;
203
192
} else {
204
- const settled = await Promise . allSettled ( ps ) . then ( ( results ) => {
205
- return results . map ( ( res , i ) => {
206
- if ( res . status === "rejected" ) {
207
- return [
208
- keys [ i ] ,
209
- {
210
- ...( res . reason as ServerError_ ) . data$ ,
211
- httpStatusCode : "500" ,
212
- } ,
213
- ] as [ string , ServerError ] ;
214
- }
215
- return [ keys [ i ] , res . value ] as [ string , SuccessfulPolicyResponse ] ;
216
- } ) ;
217
- } ) ;
218
- items = settled ;
193
+ throw err ;
219
194
}
220
- res = { responses : Object . fromEntries ( items ) } ;
221
- } else {
222
- throw err ;
223
195
}
224
196
}
225
197
@@ -231,6 +203,49 @@ export class OPAClient {
231
203
}
232
204
return Object . fromEntries ( entries ) ;
233
205
}
206
+
207
+ // run a sequence of evaluatePolicyWithInput(), via Promise.all/Promise.allSettled
208
+ async fallbackBatch < Res > (
209
+ path : string ,
210
+ inputs : { [ k : string ] : Input } ,
211
+ opts ?: BatchRequestOptions < Res > ,
212
+ ) : Promise < { [ k : string ] : ServerError | SuccessfulPolicyResponse } > {
213
+ let items : [ string , ServerError | SuccessfulPolicyResponse ] [ ] ;
214
+ const keys = Object . keys ( inputs ) ;
215
+ const ps = Object . values ( inputs ) . map ( ( input ) =>
216
+ this . opa
217
+ . executePolicyWithInput ( { path, requestBody : { input } } )
218
+ . then ( ( { successfulPolicyResponse : res } ) => res ) ,
219
+ ) ;
220
+ if ( opts ?. rejectMixed ) {
221
+ items = await Promise . all ( ps ) . then ( ( results ) =>
222
+ results . map ( ( result , i ) => {
223
+ if ( ! result ) throw `no result in API response` ;
224
+ return [
225
+ keys [ i ] as string , // can't be undefined
226
+ result ,
227
+ ] ;
228
+ } ) ,
229
+ ) ;
230
+ } else {
231
+ const settled = await Promise . allSettled ( ps ) . then ( ( results ) => {
232
+ return results . map ( ( res , i ) => {
233
+ if ( res . status === "rejected" ) {
234
+ return [
235
+ keys [ i ] ,
236
+ {
237
+ ...( res . reason as ServerError_ ) . data$ ,
238
+ httpStatusCode : "500" ,
239
+ } ,
240
+ ] as [ string , ServerError ] ;
241
+ }
242
+ return [ keys [ i ] , res . value ] as [ string , SuccessfulPolicyResponse ] ;
243
+ } ) ;
244
+ } ) ;
245
+ items = settled ;
246
+ }
247
+ return Object . fromEntries ( items ) ;
248
+ }
234
249
}
235
250
236
251
function processResult < Res > (
0 commit comments