Genesis Foundation Filters
foundation-filters
provides a collection of client-side filters.
Client-side filters in Genesis allow users to filter and process data locally within the web application. They may also access runtime data or state to provide an outcome. These filters, including options like userTargeting
and timeWindow
, enable customized data manipulation directly in the browser, enhancing responsiveness by reducing server requests.
Client-side filters generally take primitive input parameters, and return true
or false
based on these values. They can be combined using the ClientFilterRunner utility provided in the foundation-filters
package to create layered filtering conditions for complex data scenarios. When 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.
API Documentation
For more detailed information on API and configurations, please refer to the API documentation in the docs/api
directory.
Example
Lets look at an example of one of the many filters in this module, the timeWindow
filter. Further examples are shown below.
Each filter is provided in two flavours:
- Functions
- Dependency Injection (DI)
These flavours are explained in detail below with help of code examples
- 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';
}
}
The function version is pretty self-explanatory, and you may be wondering why you would use the DI injected version. The reason is at the point of calling a filter, either directly or indirectly as (say) part of a chain, you may not know what data or state from the application it needs to provide its outcome. The DI versions encapsulate 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 in Genesis's foundation-filters
package:
- nodeEnv: This filter is used to match the current environment (production, development etc.) with the environment specified (passed as function param). It is useful for supporting environment-specific conditions ensuring that certain features are only available for targetted environment.
- percentage: The percentage filter in Genesis foundation filters checks if a randomly generated number between 0 and 100 is within a specified percentage threshold. This allows for setting probabilistic conditions, enabling features or elements to be displayed to a proportion of users based on the given percentage. This filter is useful for scenarios like A/B testing or gradually rolling out new features.
- timeWindow: This filter is used to check whether the current date and time come in between 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: It is used to filter content based on URL parts like scheme, path, host, or pattern matching. It allows to target specific URLs based on particular criteria, which can be useful for enabling or restricting features.
- userAgent: It filters content based on user's browser, OS, devices or specified user agent string. It allows customized experience according to specific environments, such as targeting mobile devices audience or restricting access on a particular browser.
- userTargeting: This filters content on basis of user related attributes like usernames, permissions and profiles. It aids in showing personalized content or restricting access to specific content depending on the soecified user conditions.
Installation
To enable this module in your application, follow the steps below.
- Add
@genesislcap/foundation-filters
as a dependency in yourpackage.json
file. Whenever you change the dependencies of your project, ensure you run the$ npm run bootstrap
command again. You can find more information in the package.json basics page.
{
"dependencies": {
"@genesislcap/foundation-filters": "latest"
},
}
DI use cases
Integrating filters using Dependency Injection (DI) enables dynamic configurations and simplifies reusability across components. Below is an example of using userTargetingFilter with DI for precise user-specific logic.
import { UserTargeting } from '@genesislcap/foundation-filters';
...
@UserTargeting userTargeting: UserTargeting;
...
const outcome = this.userTargeting.filter({
profiles: ['ADMIN']
});
Here we've used the UserTargeting
filter without the need to provide the current user or other data points to the underlying function directly. If where we source the current user from changes over time, the author of the filter can refactor centrally.
All DI-based filters implement the ClientFilter interface; however, they may provide other APIs for convenience to abstract underlying implementation details. For example, UserTargeting
provides a hasAdminProfile
API, so the previous example can be re-written as:
import { UserTargeting } from '@genesislcap/foundation-filters';
...
@UserTargeting userTargeting: UserTargeting;
...
const outcome = this.userTargeting.hasAdminProfile();
This is quite a simplistic example, but we hope it helps to highlight that common parameters can be predefined in the filters themselves where needed. It is also worth mentioning that DI-based filters are registered as transient, meaning you will get a new instance for every dependency request or container.get(). The ClientFilterRunner used to run chained filters is also transient.
Filters explanations with examples:
Let's take a closer look at each filter, understanding their usage with the help of examples.
nodeEnv
The nodeEnv filter in Genesis’s foundation filters checks the current application environment (such as "development," "production," etc.) and returns true if the filter’s specified environment matches the current one. This filter ensures that certain features or configurations are available only in intended environments by managing environment-specific conditions.
Lets look at the usage examples as function and when used with DI:
- 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 other available API for nodeEnv in the API Docs
percentage
The percentage filter in Genesis foundation filters checks if a randomly generated number between 0 and 100 is within a specified percentage threshold. This allows for setting probabilistic conditions, enabling features or elements to be displayed to a proportion of users based on the given percentage. This filter is useful for scenarios like A/B testing or gradually rolling out new features.
The percentage filter allows you to display certain features or messages to a set percentage of users. For example, if you set the filter to 20%, each time it runs, it has a 20% chance of passing, meaning 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.5; // 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 is used to check whether the current date and time fall between the specified start and end period.The filter returns true if the current date and time is within the specified range. timeWindow
filter can be helpful when you want to manipulate data within a specific time frame.
Lets look at the usage examples as function and with DI:
- 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
Both params start and end can be provided in any format recognized by the Date.parse()
method.
urlTargeting
The urlTargeting
is used to filter content on the basis of URL structure, allowing you to use specific criteria to target matching URLs or patterns. Following parameters are used to configure this filter:
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 path to target.
This filter will return true when the current URL matches on ANY of the parameters explained. If you wish to match on multiple parameters, consider chaining an array of urlTargeting filters together using ClientFilterRunner.
This filter helps in targeting specific URLs based on these parameters, which 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, like https://test.genesislab.global
.
Lets look at the usage examples as function and with DI:
- 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
The userAgent
filters content on the basis of user’s browser, device, operating system (OS), or specific user agent string parameters. It allows user to specify which browsers(e.g. Chrome, Firefox), devices (e.g., mobile or desktop), or OS (e.g., IOS, Windows) have access to certain features. You can use this filter for providing tailored experience on the basis of user's environment for instance customizing content for a specic browser or device.
Lets look at the usage examples as function and with DI:
- 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. It uses the following criteria for filtering:
Usernames: filters on the basis of usernames.
Profiles: filters on the basis of assigned user profiles.
Permissions: Applies filtering on the basis of assigned permissions on user profile.
It will return true when the current user matches on ANY of the parameters mentioned above. If you wish to match on multiple parameters, consider chaining an array of userTargeting filters together using ClientFilterRunner.
- 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']
});
One use case of this filter is checking if the user is an admin. For this purpose, hasAdminProfile
utility provided by this filter can be used to determine whether a user has administrative access.
import { UserTargeting } from '@genesislcap/foundation-filters';
...
@UserTargeting userTargeting: UserTargeting;
...
const outcome = this.userTargeting.hasAdminProfile();
This filter is extremely helpful for restricting features or allowing personalized content. Another important application of this filter might be in managing access control on basis of profile, role, or permissions.
Combining filters with the runner method
The clientFilterRunner method allows you to run a combination of filters sequentially, evaluating conditions like userAgent, timeWindow, or URL patterns matches. If any filter fails(returns false
), then the entire result is false
, enabling managing 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