A filter is a condition for eligibility to participate in an experiment.

Filters may be used to determine what users will be included in your defined experiments. Our built in filters include language, geo, user-agent, individual user IDs (usually used for QA) and more.

Built-In Filters

Petri comes with various built-in filters, these filters extract and use parts of an HTTP request to make decisions about which users will be included in a defined experiment. A full list of built-in servers can be found here.

Some built-in filters let you choose which parts of an HTTP request to extract relevant data from (and then make decisions by). These filters are:

  1. GeoFilter (By Country)
  2. LanguageFilter
  3. IncludeUserIdsFilter (Filters using user IDs, this helps us open features to specific users)

Customizing data extraction for a filter

A filter may extract data from an HTTP header, a cookie, a query parameter, or a an HTTP header. Priority may also be set. Both are configured by adding configuration to (or creating) a filters.yaml file in your application WEB-INF folder.

The following is configuration for the CountryFilter:

    configs:
      Country:
      - - "Header"
        - "SOME_HEADER_NAME"
      - - "Cookie"
        - "SOME_COOKIE_NAME"
      - - "Param"
        - "SOME_PARAM_NAME"
      - - "Header"
        - "SOME_OTHER_HEADER_NAME"
      Language:
        ...

The configuration above means that the country value for the filter will be extracted using the priority set by the list: First, the reslover will try to find a header on the request with the name SOME_HEADER_NAME. Then, it will search by the cookie and so on.

Note: You may specify multiple header/cookie/param names and they will each be resolved by the order of the list. When the resolver finds a value, the solution process completes. In case the value is not resolved, the reslover defaults to the default behaviour.

The default behaviour for a resolver can be found in its defaultResolution function, as can be seen for example in CountryResolver’s defaultResolution function

In case you need any custom data conversion, you can add your own converter per each filter.

For example: if userId is passed as base64 encoded in some header, you can write a custom Converter which decodes the user id:

package com.mycomp.petri.converters
class SampleBase64UserIdConverter extends Converter[UUID] {
  def convert(value: String): UUID = UUID.fromString(new String(Base64.getDecoder.decode(value.getBytes)))
}

and add this to yaml configuration file:

    configs:
      UserId:
      - - "Header"
        - "SOME_HEADER_NAME"
      - - "Converter"
        - "com.mycomp.petri.converters.SampleBase64UserIdConverter"

Custom Filters

Add a custom filter with your own custom logic

Any filter on the classpath that answers the following criteria will be available:

  • The class should be under the root package “filters” (similar to the “specs” package)
  • The class should implement the Filter interface
  • The class should define the ‘@FilterTypeName(“id”)’ annotation

see examples:

  1. Additional Filter example
  2. Custom User Type Filter example

Another option is to add a jar containing your custom filters. This option is convenient if you are using the Laboratory as a Service.

  1. Create a directory called petri-plugins in the same location as you installed the server

    • Any jar containing ‘extended-filters’ in it’s name will be scanned for filters
    • Filters should follow the same guidelines as in the above section
  2. Do the same in the location where you installed the ‘Laboratory Service’