Skip to content
This repository was archived by the owner on Jul 1, 2024. It is now read-only.

Commit f4025a6

Browse files
committed
sdk: refactor fallback to use Promise.all[Settled]
Signed-off-by: Stephan Renatus <stephan@styra.com>
1 parent 30ba90f commit f4025a6

File tree

2 files changed

+45
-34
lines changed

2 files changed

+45
-34
lines changed

src/opaclient.ts

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
type ServerError,
77
BatchMixedResults,
88
BatchSuccessfulPolicyEvaluation,
9+
SuccessfulPolicyResponse,
910
} from "./sdk/models/components/index.js";
1011
import {
1112
ExecutePolicyWithInputResponse,
@@ -151,7 +152,7 @@ export class OPAClient {
151152
* @param inputs - The inputs to the policy.
152153
* @param opts - Per-request options to control how the policy evaluation result is to be transformed
153154
* into `Res` (via `fromResult`), if any failures in the batch result should reject the promose (via
154-
* `rejectErrors`), and low-level fetch options.
155+
* `rejectMixed`), and low-level fetch options.
155156
*/
156157
async evaluateBatch<In extends Input | ToInput, Res>(
157158
path: string,
@@ -174,41 +175,51 @@ export class OPAClient {
174175

175176
res = resp.batchMixedResults || resp.batchSuccessfulPolicyEvaluation;
176177
} catch (err) {
178+
// TODO(sr): memoize fallback
177179
if (
178180
err instanceof SDKError &&
179181
err.httpMeta.response.status == 404 &&
180182
opts?.fallback
181183
) {
182-
// run a sequence of evaluatePolicyWithInput() instead
183-
const responses: {
184-
[k: string]: ResponsesSuccessfulPolicyResponse | ServerError;
185-
} = {};
186-
for (const [k, input] of Object.entries(inps)) {
187-
try {
188-
const result = await this.opa.executePolicyWithInput(
189-
{
190-
path,
191-
requestBody: { input },
192-
},
193-
opts,
194-
);
195-
if (result.successfulPolicyResponse)
196-
responses[k] = result.successfulPolicyResponse;
197-
} catch (err) {
198-
if (err instanceof ServerError_) {
199-
responses[k] = {
200-
...err.data$,
201-
httpStatusCode: "500",
202-
};
203-
} else {
204-
throw err;
205-
}
206-
}
207-
if (!responses[k]) throw `no result in API response`;
184+
// run a sequence of evaluatePolicyWithInput() instead, via Promise.all
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),
192+
);
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+
);
203+
} 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;
208219
}
209-
res = { responses };
220+
res = { responses: Object.fromEntries(items) };
210221
} else {
211-
return Promise.reject(err);
222+
throw err;
212223
}
213224
}
214225

tests/authorizer.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ allow if {
115115
});
116116

117117
it("rejects with server error on failure", async () => {
118-
assert.rejects(
118+
await assert.rejects(
119119
new OPAClient(serverURL).evaluate("condfail/p", {
120120
a: "a",
121121
b: "a",
@@ -262,7 +262,7 @@ allow if {
262262

263263
it("allows fetch options", async () => {
264264
const signal = AbortSignal.abort();
265-
assert.rejects(
265+
await assert.rejects(
266266
new OPAClient(serverURL).evaluate("test/p_bool", undefined, {
267267
fetchOptions: { signal },
268268
}),
@@ -435,7 +435,7 @@ allow if {
435435
});
436436

437437
it("rejects mixed-mode result if instructed", async () => {
438-
assert.rejects(
438+
await assert.rejects(
439439
new OPAClient(serverURL).evaluateBatch(
440440
"condfail/p",
441441
{
@@ -474,7 +474,7 @@ allow if {
474474

475475
describe("batch-fallback", () => {
476476
it("fails unless instructed to fallback", async () => {
477-
assert.rejects(
477+
await assert.rejects(
478478
new OPAClient(serverURL).evaluateBatch("test/p_bool_false", {
479479
a: false,
480480
b: false,
@@ -543,7 +543,7 @@ allow if {
543543
});
544544

545545
it("rejects mixed-mode result if instructed", async () => {
546-
assert.rejects(
546+
await assert.rejects(
547547
new OPAClient(serverURL).evaluateBatch(
548548
"condfail/p",
549549
{

0 commit comments

Comments
 (0)