No description or website provided.
Java
Latest commit b183ffc Nov 28, 2016 Yiqing Zhu PIPET-57: upgrade to spring 4

README.md

Proctor Pipet

Proctor Pipet is a Java web application that provides a simple REST API to Proctor.

Through this API, other programming languages only need an HTTP client to use Proctor. This makes it easy to get test groups from a non-JVM web backend language like PHP or Python.

Getting Started

  1. git clone the repository or download the sources.

  2. Copy the three configuration files in proctor-pipet-deploy/src/main/webapp/WEB-INF/example/ to any directory.

    These contain some basic Proctor tests that let you quickly try out Proctor Pipet.

  3. Edit the paths in proctor-pipet-deploy/src/main/webapp/WEB-INF/config/pipet-base.properties to point to that directory.

    proctor.test.matrix.path=/your/path/to/proctor-tests-matrix.json
    proctor.pipet.config.path=/your/path/to/pipet-config.json
    proctor.pipet.reload.seconds=10
  4. Run mvn package to create a .war package.

  5. Run java -jar proctor-pipet-deploy/target/dependency/webapp-runner.jar proctor-pipet-deploy/target/*.war. This starts a local web server.

  6. Visit http://localhost:8080/groups/identify?ctx.country=US&ctx.loggedIn=true&id.USER=pa5xq0lz4n80 in your browser.

    Like determineTestGroups(), this returns group assignments given context variables and identifiers.

    If you refresh, group assignments stay the same. This ensures your users don't change test groups as they browse.

    Try adding random characters to id.USER (which typically represents a tracking cookie). The assigned test groups will change.

    buttoncolortst has a string payload attached to it. You can use payloads to try different colors or text by changing the test matrix. No redeploy of your web application is needed!

    The user agent is parsed and can be used in more complex Proctor rules. For example, the mobileonly test only appears if the user agent is a mobile device.

    You can configure the API to accept context variables and identifiers from any source in pipet-config.json. The example is configured to look at the HTTP header User-Agent, but this can come from any header name or query parameter.

Configuration

There are three configuration files required by Proctor Pipet.

pipet.properties

This is a simple ini-like file that defines paths to other configuration files and sets some other simple properties.

Proctor Pipet searches for this properties file in the following places in this order:

  1. WEB-INF/config/pipet-base.properties (This is included in the repository. The included default sets the path of the configuration files to the ${catalina.base}/conf/proctor/ directory.)

  2. ${catalina.base}/conf/pipet.properties

  3. Path pointed to by the propertyPlaceholderResourceLocation Tomcat context parameter.

The properties file requires the following fields:

  • proctor.test.matrix.path

    Path to the Proctor test matrix.

  • proctor.pipet.config.path

    Path to the Pipet configuration.

  • proctor.pipet.reload.seconds

    An integer defining the number of seconds between reloads of the Proctor test matrix.

    Decreasing this number will increase the frequency of checking whether the test matrix has changed.

Test Matrix

A JSON file with a list of all test definitions used by Proctor Pipet, including all test buckets and payload values.

Proctor Webapp provides a visual interface for viewing and editing the test matrix.

See: Proctor Test Matrix Schema

Pipet Configuration

A JSON file that describes the variables that users pass into the /groups/identify API call, including whether they are passed in as a query parameter or as a header.

Here is a full example of a Pipet configuration:

{
    "context": {
        "country": {
            "source": "QUERY",
            "type": "String"
        },
        "loggedIn": {
            "source": "QUERY",
            "type": "boolean",
            "defaultValue": false
        },
        "userAgent": {
            "source": "HEADER",
            "sourceKey": "User-Agent",
            "type": "UserAgent"
        }
    },
    "identifiers": {
        "USER": {
            "source": "QUERY"
        },
        "ACCOUNT": {
            "source": "QUERY",
            "sourceKey": "acctid"
        }
    }
}

context contains a map of all context variables, and identifiers contains a map of all identifiers.

For context variables, the key is the name of the context variable, which is also used for Proctor rules in the test matrix.

For identifiers, the key is the test type, such as USER or ACCOUNT.

Configuration Values

All variables support the following configuration values:

  • source (required)

    Where Proctor Pipet looks for this variable in the /groups/identify web request.

    Acceptable values: QUERY or HEADER

  • sourceKey (optional)

    At which key Proctor Pipet looks for this variable in the source.

    For QUERY, this is the name of the query parameter. For HEADER, this is the header name.

    If the sourceKey is not specified, the source key is defaulted to this configuration's key. For example, country's default source key is country.

Context variables support additional configuration values:

  • type (required)

    Name of the type this context variable will be converted to for evaluation in Proctor rules.

    Acceptable values include any of the eight Java primitive types, their object wrapper classes, String, and UserAgent.

  • defaultValue (optional)

    If this context variable is not included in the request, this default value will be used for that variable.

    If a defaultValue is not specified, then this context variable is required and must be included in every /groups/identify API call.

    The JSON type of this value does not matter.

Deploying New Variables

All context variables that don't have a defaultValue must be provided in the API request. When you add a new context variable without a defaultValue to the Pipet configuration, all API users are immediately required to include it on every request. Otherwise, the API will return an error.

To avoid this, before deploying a new context variable, make sure that every user of the API includes that variable in their requests. After that, you can safely deploy Pipet without causing API errors.

API Endpoints

All API responses are wrapped in a JSON envelope with data and meta fields.

{
    "data": {
        ...
    },
    "meta": {
        "status": 200
    }
}

data contains the true content of the API response. data is always present, but it can be empty.

meta contains metadata about the API response. meta.status contains the HTTP response code.

If there was an error with the API request, meta.error will contain a string with an error message. For example:

{
    "data": { },
    "meta": {
        "status": 400,
        "error": "Request must have at least one identifier."
    }
}

GET /groups/identify

From given identifiers and context variables, determine the test groups that should be used.

Parameters

In addition to the query parameters below, the Pipet configuration can declare that certain variables come from the request headers instead.

  • ctx.{sourceKey} (context variables without a defaultValue are required)

    All query parameters starting with ctx. are treated as context variables and converted to the type specified in the Pipet configuration.

    Context variables are used in evaluating Proctor rule expressions.

    Unrecognized sourceKeys that are not in the Pipet configuration are ignored by Pipet.

  • id.{sourceKey} (at least one identifier is required)

    All query parameters starting with id. are treated as identifiers.

    Identifiers are used to differentiate different users based on tracking cookie, account id, email, or anything else that is supported by Proctor.

    Unrecognized sourceKeys that are not in the Pipet configuration are ignored by Pipet.

  • test (optional)

    Filter the returned tests by name.

    A comma-separated list of test names.

    Use this parameter to return only the tests that your web application is interested in and can supply the relevant context variables for rules.

    If this parameter is not present, no filter is applied, and the API attempts to return all tests in the test matrix. If your test matrix is large and contains tests and rules from many different projects, this may be a bad idea. If a rule fails to execute because a context variable was missing, the API will log an error message to the logfile/console, skip the test, and continue.

    If this parameter is empty, zero tests are returned, so groups will be empty. The audit information is still accurate.

  • prforceGroups (optional)

    Force certain test group assignments in this API request. This lets privileged users (developers) test their groups.

    This parameter works exactly like Proctor's prforceGroups.

    Formatted as a comma-separated list of groups strings in the format of {groupName}{bucketValue}. For example: prforceGroups=buttoncolortst2,newfeaturerollout0

    This parameter is deliberately simple so that your web backend can easily implement force groups like Proctor:

    1. If the user is privileged and includes a prforceGroups query parameter in their request, store its value in a session cookie and use the value in this API call.

    2. If the user is privileged and has that cookie and does not use the query parameter, use the cookie's value in this API call.

      If you use a simple cookie instead of signed cookies or sessions IDs, ensure that you check for user privilege before using the cookie value. Otherwise this is a security issue because ordinary users could manually set that cookie to force groups.

    3. Otherwise, do not include the prforceGroups parameter (or use a blank string).

Here is an example of a request using all of these parameters:

GET /groups/identify
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:28.0) Gecko/20100101 Firefox/28.0

ctx.country=US
ctx.loggedIn=true
id.USER=pa5xq0lz4n80
id.acctid=5083
test=buttoncolortst,newfeaturerollout,mobileonly
prforceGroups=buttoncolortst2,newfeaturerollout0

Response

{
    "data": {
        "groups": {
            ...
        },
        "context": {
            "userAgent": {
                ...
            },
            "loggedIn": true,
            "country": "US"
        },
        "audit": {
            "version": "1",
            "updated": 1,
            "updatedBy": "example"
        },
    "meta": {
        "status": 200
    }
}

The response to the /groups/identify API call contains three major components:

groups

groups contains a mapping of test name to values associated with the test group assignment.

{
    "buttoncolortst": {
        "name": "control",
        "value": 0,
        "version": "1",
        "payload": {
            "stringValue": "#C0C0C0"
        }
    },
    "newfeaturerollout": {
        "name": "inactive",
        "value": -1,
        "version": "1"
    }
}

The values include the bucket name, the bucket value, the version of the test definition, and possibly a payload.

payload is only included if the test definition contains payload values and a payload type.

If test groups you are expecting are not in the groups mapping, it's possible that an eligibility rule excludes them, or there was no identifier with the proper test type. Ensure that you have appropriate default behavior for these situations. This default behavior would also be useful if your Proctor Pipet instance goes down or stops responding.

context

context contains all the context variables with their converted values.

The user agent output is especially helpful to help debug any rules based on user agent.

audit

audit contains the audit of the test matrix just like /proctor/matrix/audit.

This can be useful for caching.

GET /proctor/matrix

Returns the entire test matrix.

GET /proctor/matrix/audit

Returns the audit of the test matrix.

The response includes a version string, an updated timestamp, and an updatedBy name.

GET /proctor/matrix/definition/{testname}

Returns the definition for a specific test as defined in the test matrix.

GET /config/context

Returns the configured context variables from the Pipet configuration.

GET /config/identifiers

Returns the identifiers from the Pipet configuration.

See Also