mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
fix: support response query filtering in safe mode (#7441)
* fix(js): support response query filters in safe mode * feat: add tests and improve bruno-response shim for query argument handling - Introduced a new test suite for the bruno-response shim to validate query filtering and function callback behavior. - Refactored the `res` function to enhance argument handling, allowing for better marshalling of QuickJS function handles and other values. - Improved error handling within the response processing to ensure robust behavior during query execution. * fix: correct argument handling in bruno-response shim for function callbacks - Updated the `toHostQueryArg` function to use `vm.undefined` instead of `vm.global` when calling the provided function argument. This change ensures proper context handling during function execution, improving the reliability of query argument processing. * chore: clean up .gitignore and enhance error handling in bruno-response tests - Removed redundant entries from .gitignore to streamline ignored files. - Improved error handling in the `afterEach` and `afterAll` hooks of the bruno-response tests to ensure proper disposal of resources, preventing potential memory leaks. --------- Co-authored-by: cryst <230207759+cryst-hq@users.noreply.github.com>
This commit is contained in:
@@ -1,8 +1,35 @@
|
||||
const { marshallToVm } = require('../utils');
|
||||
|
||||
// Marshal a QuickJS query argument to a host-compatible value.
|
||||
// Function handles are wrapped as native callbacks; other values are dumped as-is.
|
||||
// Safe because @usebruno/query's get() invokes filters synchronously,
|
||||
// so the borrowed arg handle is still valid.
|
||||
const toHostQueryArg = (vm, arg) => {
|
||||
if (vm.typeof(arg) === 'function') {
|
||||
return (item) => {
|
||||
const itemHandle = marshallToVm(item, vm);
|
||||
const result = vm.callFunction(arg, vm.undefined, itemHandle);
|
||||
itemHandle.dispose();
|
||||
|
||||
if (result.error) {
|
||||
const error = vm.dump(result.error);
|
||||
result.error.dispose();
|
||||
throw error;
|
||||
}
|
||||
|
||||
const value = vm.dump(result.value);
|
||||
result.value.dispose();
|
||||
return value;
|
||||
};
|
||||
}
|
||||
|
||||
return vm.dump(arg);
|
||||
};
|
||||
|
||||
const addBrunoResponseShimToContext = (vm, res) => {
|
||||
let resFn = vm.newFunction('res', function (exprStr) {
|
||||
return marshallToVm(res(vm.dump(exprStr)), vm);
|
||||
let resFn = vm.newFunction('res', function (exprStr, ...queryArgs) {
|
||||
const nativeArgs = queryArgs.map((arg) => toHostQueryArg(vm, arg));
|
||||
return marshallToVm(res(vm.dump(exprStr), ...nativeArgs), vm);
|
||||
});
|
||||
|
||||
const status = marshallToVm(res?.status, vm);
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
const { describe, it, expect, beforeAll, beforeEach, afterEach, afterAll } = require('@jest/globals');
|
||||
const { newQuickJSWASMModule } = require('quickjs-emscripten');
|
||||
const addBrunoResponseShimToContext = require('./bruno-response');
|
||||
|
||||
describe('bruno response shim', () => {
|
||||
let vm, module;
|
||||
|
||||
beforeAll(async () => {
|
||||
module = await newQuickJSWASMModule();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vm = module.newContext();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (vm) {
|
||||
try {
|
||||
vm.dispose();
|
||||
} catch (err) {
|
||||
console.error('Error disposing vm', err);
|
||||
}
|
||||
vm = null;
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
if (module) {
|
||||
try {
|
||||
module.dispose();
|
||||
} catch (err) {
|
||||
console.error('Error disposing module', err);
|
||||
}
|
||||
module = null;
|
||||
}
|
||||
});
|
||||
|
||||
it('forwards response query filter callbacks in safe mode', () => {
|
||||
const resources = [
|
||||
{ id: 'test', value: 1 },
|
||||
{ id: 'other', value: 2 }
|
||||
];
|
||||
const response = Object.assign(
|
||||
(expr, filter) => {
|
||||
expect(expr).toBe('resources[?].id');
|
||||
return resources.filter(filter).map((item) => item.id);
|
||||
},
|
||||
{ status: 200, statusText: 'OK', headers: {}, body: { resources } }
|
||||
);
|
||||
|
||||
addBrunoResponseShimToContext(vm, response);
|
||||
|
||||
const result = vm.evalCode(`
|
||||
res('resources[?].id', r => r.id === 'test')
|
||||
`);
|
||||
|
||||
const valueHandle = vm.unwrapResult(result);
|
||||
const filtered = vm.dump(valueHandle);
|
||||
valueHandle.dispose();
|
||||
|
||||
expect(filtered).toEqual(['test']);
|
||||
});
|
||||
|
||||
it('still supports object predicates in safe mode', () => {
|
||||
const resources = [
|
||||
{ id: 'test', value: 1 },
|
||||
{ id: 'other', value: 2 }
|
||||
];
|
||||
const response = Object.assign(
|
||||
(expr, predicate) => {
|
||||
expect(expr).toBe('resources[?].id');
|
||||
return resources.filter((item) =>
|
||||
Object.entries(predicate).every(([key, value]) => item[key] === value)
|
||||
).map((item) => item.id);
|
||||
},
|
||||
{ status: 200, statusText: 'OK', headers: {}, body: { resources } }
|
||||
);
|
||||
|
||||
addBrunoResponseShimToContext(vm, response);
|
||||
|
||||
const result = vm.evalCode(`
|
||||
res('resources[?].id', { id: 'other' })
|
||||
`);
|
||||
|
||||
const valueHandle = vm.unwrapResult(result);
|
||||
const filtered = vm.dump(valueHandle);
|
||||
valueHandle.dispose();
|
||||
|
||||
expect(filtered).toEqual(['other']);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user