Filters
foundation-filters
provides a collection of client-side filters.
These client-side filters enable the client app to filter and process data locally on behalf of the user.
In general, client-side filters take primitive input parameters, and return true
or false
based on these values. You can combine them using the ClientFilterRunner utility provided in the foundation-filters
package to create layered filtering conditions for complex data scenarios.
If a filter is used without all the input parameters needed, Genesis logs a warning and returns true
by convention so that the chain can continue.
For detailed information on the API and configuration, see the API documentation.
Example
Here is an example of the timeWindow
filter. In this case, the start and date are created dynamically; they show the day before the current day and the day after by default. You can click on the Check
button to see the message returned. And you can change the dates to see a different message when today's date is not in the range.
Each filter is provided in two flavours:
- Functions
- Dependency injection (DI)
Both flavours are shown in the example below. This also uses timeWindow
, but in this case, the start and end for the period are both set to a specific date and time.
- Dependency Injection (DI)
- Function
import { timeWindowFilter } from '@genesislcap/foundation-filters';
@customElement({
name: 'my-element',
template: html`
<div class="container">
<rapid-text-field type="datetime-local" value=${sync((x) => x.startDate)}>
Start Period
</rapid-text-field>
<rapid-text-field type="datetime-local" value=${sync((x) => x.endDate)}>
End Period
</rapid-text-field>
<rapid-button @click=${(x) => x.checkWindow()}>Check</rapid-button>
<span>${(x) => x.resultString}</span>
</div>
`,
})
export class MyElement extends GenesisElement {
@observable startDate: string = "2024-11-21T09:00";
@observable endDate: string = "2024-11-21T18:00";
@observable resultString: string;
checkWindow() {
this.resultString = timeWindowFilter(this.startDate, this.endDate)
? 'Exists within the start and end period'
: 'Doesnot fall between start and end period';
}
}
import { TimeWindow } from '@genesislcap/foundation-filters';
@customElement({
name: 'my-element',
template: html`
<div class="container">
<rapid-text-field type="datetime-local" value=${sync((x) => x.startDate)}>
Start Period
</rapid-text-field>
<rapid-text-field type="datetime-local" value=${sync((x) => x.endDate)}>
End Period
</rapid-text-field>
<rapid-button @click=${(x) => x.checkWindow()}>Check</rapid-button>
<span>${(x) => x.resultString}</span>
</div>
`,
})
export class MyElement extends GenesisElement {
@observable startDate: string = "2024-11-21T09:00";
@observable endDate: string = "2024-11-21T18:00";
@observable resultString: string;
@TimeWindow timeWindow: TimeWindow;
checkWindow() {
this.resultString = this.timeWindow.filter({start: this.startDate, end: this.endDate})
? 'Exists within the start and end period'
: 'Doesnot fall between start and end period';
}
}
In the example above, the function version is pretty self-explanatory, so why you would use the DI injected version?
Functions work well if you know exactly what data or states are needed to achieve the outcome. However, at the point of calling a filter, you might not necessarily know what data or states are required.
The DI versions enable you to handle this complexity, which aids later refactoring, and leaves callers free to pass only primitive input parameters where possible.
Available filters
Here is a list of available filters:
- nodeEnv: This filter is used to match the current environment (production, development etc.) with the environment specified ( which is passed as function param). It is useful for supporting environment-specific conditions so that certain features are only available for targeted environment.
- percentage: The percentage filter checks if a randomly generated number between 0 and 100 is within a specified percentage threshold. This enables you to set probabilistic conditions, so that features or elements can be displayed to a proportion of users based on the given percentage. This filter is useful for scenarios such as A/B testing or gradually rolling out new features.
- timeWindow: This filter checks whether the current date and time come fall within the start and end period specified in the parameters. The filter returns true if the current date and time falls between the range, thus making it useful for time-sensitive conditions.
- urlTargeting: This filters content by URL parts, such as scheme, path, host, or pattern matching. It enables you to target specific URLs, which can be useful for enabling or restricting features.
- userAgent: This filters content by the user's browser, OS, devices or specified user agent string. For example, you can customize the experience according to specific environments, such as targeting mobile devices or audience, or you can restrict access from a specific browser.
- userTargeting: This filters content on basis of attributes such as usernames, permissions and profiles. It enables you to show personalized content or to restrict access to specific content depending on user conditions.
Using dependency injection
Dependency injection (DI) is essential if you want to set up filters with dynamic configurations. This method also simplifies reusability across components. The example below uses the UserTargeting
filter without the need to provide the current user or other data points directly to the underlying function. If you later change the source of the the current user, you can refactor centrally.
import { UserTargeting } from '@genesislcap/foundation-filters';
...
@UserTargeting userTargeting: UserTargeting;
...
const outcome = this.userTargeting.filter({
profiles: ['ADMIN']
});
All DI-based filters implement the ClientFilter interface. However, you can provide other APIs for convenience to abstract underlying implementation details. For example, UserTargeting
provides a hasAdminProfile
API, so you could rewrite the previous example as:
import { UserTargeting } from '@genesislcap/foundation-filters';
...
@UserTargeting userTargeting: UserTargeting;
...
const outcome = this.userTargeting.hasAdminProfile();
This is quite a simplistic example, but it highlights that common parameters can be predefined in the filters themselves where needed.
Also note that DI-based filters are registered as transient, so you will get a new instance for every dependency request or container.get()
. The ClientFilterRunner
used to run chained filters is also transient.
Available filters
This section looks at each filter in detail and provides examples.
nodeEnv
The nodeEnv filter checks the NODE_ENV
environment variable to obtain the current application environment. If this is unset, then it is classed as development
.
The filter returns true if the filter’s specified environment matches the current one. Use this, for example, to ensure that specific features or configurations are available only to the intended environments.
Here is an example:
- Dependency Injection (DI)
- Function
import { nodeEnvFilter } from '@genesislcap/foundation-filters';
...
const outcome = nodeEnvFilter(['development']);
import { NodeEnv } from '@genesislcap/foundation-filters';
...
@NodeEnv nodeEnv: NodeEnv;
...
const outcome = this.nodeEnv.filter({envs:[ 'development']});
The nodeEnv also provides a isDevelopment
variable that can be used directly so the above example can be rewritten as:
import { NodeEnv } from '@genesislcap/foundation-filters';
...
@NodeEnv nodeEnv: NodeEnv;
...
const outcome = this.nodeEnv.isDevelopment;
You can find the other available API for nodeEnv in the API Docs.
percentage
The percentage filter checks if a randomly generated number between 0 and 100 is within a specified percentage threshold. This enables you to set probabilistic conditions, so that specified features or elements can be displayed to a proportion of users based on the given percentage.
For example, if you set the filter to 20%, then each time it runs, it has a 20% chance of passing. Consequently only about 20% of users will see the feature or content. This is useful for controlled feature rollouts or A/B testing without requiring complex set-ups.
- Dependency Injection (DI)
- Function
import { percentageFilter } from '@genesislcap/foundation-filters';
...
const probability = 0.2; // define percentage between 0 & 1
const randomNumber = Math.random(); // provide a random number optionally
const outcome = percentageFilter(probability, randomNumber);
console.log(outcome); // true or false based on probability
import { Percentage } from '@genesislcap/foundation-filters';
...
@Percentage percentageFilter: Percentage;
...
const probability = 0.5; // define percentage between 0 & 1
const randomNumber = Math.random(); // provide a random number optionally
const outcome = this.percentageFilter.filter({percent: probability, random: randomNumber});
console.log(outcome); // true or false based on probability
timeWindow
This filter checks whether the current date and time fall within the specified period. The filter returns either true or false.
There are two parameters to enable you to set the period: start
and end
. You can provide these in any format that is recognized by the Date.parse()
method.
Here is an example:
- Dependency Injection (DI)
- Function
import { timeWindowFilter } from '@genesislcap/foundation-filters';
...
const outcome = timeWindowFilter('Sun Sep 25 2022 16:51:55 GMT+0000', 'Fri Nov 25 2022 17:51:55 GMT+0000');
console.log(outcome); // returns true if current date/time falls within the start and end period
import { TimeWindow } from '@genesislcap/foundation-filters';
...
@TimeWindow timeWindow: TimeWindow;
...
const outcome = this.timeWindow.filter({
start: 'Sun Sep 25 2022 16:51:55 GMT+0000',
end: 'Fri Nov 25 2022 17:51:55 GMT+0000',
});
console.log(outcome); // returns true if current date/time falls within the start and end period
urlTargeting
urlTargeting
filters content on the basis of URL structure. You can use specific parameters to target the required URLs or patterns. The parameters for this filter are:
Parameter | Description |
---|---|
url | A full URL. |
pattern | A regex for matching URLs. |
schemes | An array of schemes or protocols to target (e.g. "http"). |
hosts | An array of host names or domain names. |
paths | A list of paths to target. |
This filter returns true when the current URL matches ANY of the parameters explained. If you wish to match on multiple parameters, consider chaining an array of urlTargeting
filters using ClientFilterRunner.
You can target specific URLs using these parameters. This can be extremely useful for restricting or enabling features based on URL parameters or domains.
For example, you could use the filter to only allow access to certain content when the URL matches a secure HTTPS connection on specific hosts, such as https://test.genesislab.global
.
Here is an example:
- Dependency Injection (DI)
- Function
import { urlTargetingFilter } from '@genesislcap/foundation-filters';
...
const outcome = urlTargetingFilter(
'https://test.com/inbox',
/test\.com\/dashboard/,
['https','http'],
['test.com'],
['dashboard','inbox']
);
console.log(outcome); // returns true if any one of the above parameters matches with the passed url
import { URLTargeting } from '@genesislcap/foundation-filters';
...
@URLTargeting urlTargeting: URLTargeting;
...
const outcome = this.urlTargeting.filter({
url: 'https://www.test.com',
schemes: ['http', 'https'],
hosts: ['test.com']
});
console.log(outcome); // returns true if any one of the above parameters matches with the passed url
userAgent
userAgent
filters content on the basis of user’s browser, device, operating system (OS), or user agent.
You can use this to specify to control access to certain features by device or operating system (for example). So you can providing tailored experience based on these criteria.
The parameters for this filter are:
Parameter | Type | Description |
---|---|---|
browsers | String array | One or more browser names, for example: 'Chrome', 'Firefox'. |
devices | String array | One or more device names, for example: 'Mobile', 'Tablet'. |
oss | String array | One or more names of operating systems, for example: 'IOS', 'Android' |
ua | String | A user-agent string, for example: navigator.userAgent . |
Here is an example:
- Dependency Injection (DI)
- Function
import { userAgentFilter } from '@genesislcap/foundation-filters';
...
const outcome = userAgentFilter(
['Chrome', 'Firefox'],
['mobile', 'tablet'],
['Android', 'Window'],
navigator.userAgent
);
console.log(outcome); // returns true if any one of the above parameters matches with the userAgent string
import { UserAgent } from '@genesislcap/foundation-filters';
...
@UserAgent userAgent: UserAgent;
...
const outcome = this.userAgent.filter({
browsers: ['Firefox'],
devices: ['mobile', 'tablet'],
oss: ['Mac OS', 'Android', 'Window'],
ua: navigator.userAgent
});
console.log(outcome); // returns true if any one of the above parameters matches with the passed userAgent string
userTargeting
This filter is used to filter content on the basis of user-specific attributes. This filter is extremely helpful for:
- restricting features to specific user profiles or permissions
- accessing personalized content.
- managing access control on basis of profile, role, or permissions.
The following parameters can be used as the criteria for filtering:
Parameter | Description |
---|---|
Profiles | Filters on the basis of assigned user profiles. |
Permissions | Applies filtering on the basis of assigned permissions on user profile. |
The filter returns true when the current user matches EITHER of the parameters. If you wish to match on multiple parameters, consider chaining an array of userTargeting
filters using ClientFilterRunner.
Here is an example:
- Dependency Injection (DI)
- Function
import { userTargetingFilter } from '@genesislcap/foundation-filters';
import { DefaultUser } from '@genesislcap/foundation-user';
...
const adminUser = new DefaultUser(); // create a mock admin user
adminUser.set({
userName: 'JohnDoe',
profile: ['ADMIN'],
permission: ['ExportClientDashboard'],
});
const outcome = userTargetingFilter(adminUser, ['JohnDoe', 'JaneDoe'], ['ADMIN', 'SUPERUSER']);
console.log(outcome); // returns true if any one of the above parameters matches with the passed admin user
import { UserTargeting } from '@genesislcap/foundation-filters';
...
@UserTargeting userTargeting: UserTargeting;
...
const outcome = this.userTargeting.filter({
profiles: ['ADMIN']
});
Targeting admin users
The userTargeting
filter provides the hasAdminProfile
utility so that you can check if the user is an admin. Here is an example of this in use:
import { UserTargeting } from '@genesislcap/foundation-filters';
...
@UserTargeting userTargeting: UserTargeting;
...
const outcome = this.userTargeting.hasAdminProfile();
Combining filters with the runner method
The clientFilterRunner method enables you to run a combination of filters sequentially. If any filter fails (returns false
), then the entire result is false
. This enables you to manage complex filtering logic in one function.
Example
import { ClientFilterRunner } from '@genesislcap/foundation-filters';
...
@ClientFilterRunner runner: ClientFilterRunner;
const outcome = this.runner.run([
{
name: 'userAgent',
parameters: {
browsers: ['Chrome'],
},
},
{
name: 'timeWindow',
parameters: {
start: '2024-11-21',
end: '2024-12-31',
},
},
]);
console.log(outcome); // true if all the filters pass
Installation
To enable this module in your application, follow the steps below.
- Add
@genesislcap/foundation-filters
as a dependency in yourpackage.json
file.
{
"dependencies": {
"@genesislcap/foundation-filters": "latest"
},
}
- Run the
$ npm run bootstrap
command. (Whenever you change the dependencies of your project, you should always run this command to rebuild.) You can find more information in the package.json basics page.