Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(jmespath): refactor custom function introspection to work with minification #2384

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions packages/jmespath/src/Functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ import { arityCheck, typeCheck } from './utils.js';
* ```
*/
class Functions {
/**
* A set of all the custom functions available in this and all child classes.
*/
public methods: Set<string> = new Set();

/**
* Get the absolute value of the provided number.
*
Expand Down Expand Up @@ -528,14 +532,32 @@ class Functions {
return Object.values(arg);
}

/**
* Lazily introspects the methods of the class instance and all child classes
* to get the names of the methods that correspond to JMESPath functions.
*
* This method is used to get the names of the custom functions that are available
* in the class instance and all child classes. The names of the functions are used
* to create the custom function map that is passed to the JMESPath search function.
*
* The method traverses the inheritance chain going from the leaf class to the root class
* and stops when it reaches the `Functions` class, which is the root class.
*
* In doing so, it collects the names of the methods that start with `func` and adds them
* to the `methods` set. Finally, when the recursion collects back to the current instance,
* it adds the collected methods to the `this.methods` set so that they can be accessed later.
*
* @param scope The scope of the class instance to introspect
*/
public introspectMethods(scope?: Functions): Set<string> {
const prototype = Object.getPrototypeOf(this);
const ownName = prototype.constructor.name;
const methods = new Set<string>();
if (ownName !== 'Functions') {
if (this instanceof Functions) {
for (const method of prototype.introspectMethods(scope)) {
methods.add(method);
}
} else {
return methods;
}

// This block is executed for every class in the inheritance chain
Expand Down
24 changes: 21 additions & 3 deletions packages/jmespath/tests/unit/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,6 @@ describe('Coverage tests', () => {
it('uses the custom function extending the powertools custom functions', () => {
// Prepare
class CustomFunctions extends PowertoolsFunctions {
public constructor() {
super();
}
@PowertoolsFunctions.signature({
argumentsSpecs: [['string']],
})
Expand Down Expand Up @@ -384,5 +381,26 @@ describe('Coverage tests', () => {
// Assess
expect(messages).toStrictEqual(['hello world']);
});

it('correctly registers all the custom functions', () => {
// Prepare
class CustomFunctions extends PowertoolsFunctions {
@PowertoolsFunctions.signature({
argumentsSpecs: [['string']],
})
public funcPassThrough(value: string): string {
return value;
}
}

// Act
const customFunctions = new CustomFunctions();
search('pass_through(foo)', { foo: 'bar' }, { customFunctions });

// Assess
expect(customFunctions.methods.size).toBeGreaterThan(
new PowertoolsFunctions().methods.size
);
});
});
});