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

Commit

Permalink
sdk: refactor fallback to use Promise.all[Settled]
Browse files Browse the repository at this point in the history
Signed-off-by: Stephan Renatus <[email protected]>
  • Loading branch information
srenatus committed Jun 26, 2024
1 parent 30ba90f commit f4025a6
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 34 deletions.
69 changes: 40 additions & 29 deletions src/opaclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
type ServerError,
BatchMixedResults,
BatchSuccessfulPolicyEvaluation,
SuccessfulPolicyResponse,
} from "./sdk/models/components/index.js";
import {
ExecutePolicyWithInputResponse,
Expand Down Expand Up @@ -151,7 +152,7 @@ export class OPAClient {
* @param inputs - The inputs to the policy.
* @param opts - Per-request options to control how the policy evaluation result is to be transformed
* into `Res` (via `fromResult`), if any failures in the batch result should reject the promose (via
* `rejectErrors`), and low-level fetch options.
* `rejectMixed`), and low-level fetch options.
*/
async evaluateBatch<In extends Input | ToInput, Res>(
path: string,
Expand All @@ -174,41 +175,51 @@ export class OPAClient {

res = resp.batchMixedResults || resp.batchSuccessfulPolicyEvaluation;
} catch (err) {
// TODO(sr): memoize fallback
if (
err instanceof SDKError &&
err.httpMeta.response.status == 404 &&
opts?.fallback
) {
// run a sequence of evaluatePolicyWithInput() instead
const responses: {
[k: string]: ResponsesSuccessfulPolicyResponse | ServerError;
} = {};
for (const [k, input] of Object.entries(inps)) {
try {
const result = await this.opa.executePolicyWithInput(
{
path,
requestBody: { input },
},
opts,
);
if (result.successfulPolicyResponse)
responses[k] = result.successfulPolicyResponse;
} catch (err) {
if (err instanceof ServerError_) {
responses[k] = {
...err.data$,
httpStatusCode: "500",
};
} else {
throw err;
}
}
if (!responses[k]) throw `no result in API response`;
// run a sequence of evaluatePolicyWithInput() instead, via Promise.all
let items: [string, ServerError | SuccessfulPolicyResponse][];
const inputs = Object.values(inps);
const keys = Object.keys(inps);
const ps = inputs.map((input) =>
this.opa
.executePolicyWithInput({ path, requestBody: { input } })
.then(({ successfulPolicyResponse: res }) => res),
);
if (opts?.rejectMixed) {
items = await Promise.all(ps).then((results) =>
results.map((result, i) => {
if (!result) throw `no result in API response`;
return [
keys[i] as string, // can't be undefined
result,
];
}),
);
} else {
const settled = await Promise.allSettled(ps).then((results) => {
return results.map((res, i) => {
if (res.status === "rejected") {
return [
keys[i],
{
...(res.reason as ServerError_).data$,
httpStatusCode: "500",
},
] as [string, ServerError];
}
return [keys[i], res.value] as [string, SuccessfulPolicyResponse];
});
});
items = settled;
}
res = { responses };
res = { responses: Object.fromEntries(items) };
} else {
return Promise.reject(err);
throw err;
}
}

Expand Down
10 changes: 5 additions & 5 deletions tests/authorizer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ allow if {
});

it("rejects with server error on failure", async () => {
assert.rejects(
await assert.rejects(
new OPAClient(serverURL).evaluate("condfail/p", {
a: "a",
b: "a",
Expand Down Expand Up @@ -262,7 +262,7 @@ allow if {

it("allows fetch options", async () => {
const signal = AbortSignal.abort();
assert.rejects(
await assert.rejects(
new OPAClient(serverURL).evaluate("test/p_bool", undefined, {
fetchOptions: { signal },
}),
Expand Down Expand Up @@ -435,7 +435,7 @@ allow if {
});

it("rejects mixed-mode result if instructed", async () => {
assert.rejects(
await assert.rejects(
new OPAClient(serverURL).evaluateBatch(
"condfail/p",
{
Expand Down Expand Up @@ -474,7 +474,7 @@ allow if {

describe("batch-fallback", () => {
it("fails unless instructed to fallback", async () => {
assert.rejects(
await assert.rejects(
new OPAClient(serverURL).evaluateBatch("test/p_bool_false", {
a: false,
b: false,
Expand Down Expand Up @@ -543,7 +543,7 @@ allow if {
});

it("rejects mixed-mode result if instructed", async () => {
assert.rejects(
await assert.rejects(
new OPAClient(serverURL).evaluateBatch(
"condfail/p",
{
Expand Down

0 comments on commit f4025a6

Please sign in to comment.