Skip to content

Commit

Permalink
fix(jmespath): refactor custom function introspection to work with mi…
Browse files Browse the repository at this point in the history
…nification (#2384)

* fix(jmespath): preserve inheritance when introspecting methods

* docs: document method

* chore: update test case

* chore: formatting

* chore: fix test
  • Loading branch information
dreamorosi authored Apr 17, 2024
1 parent 8145bc1 commit 21ecc4f
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 5 deletions.
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
);
});
});
});

0 comments on commit 21ecc4f

Please sign in to comment.