UNPKG

haraka-results

Version:

Haraka results store for connections and transactions

254 lines (182 loc) 6.63 kB
# haraka-results [![Build Status][ci-img]][ci-url] [![Code Coverage][cov-img]][cov-url] [![Code Climate][clim-img]][clim-url] Add, log, retrieve, and share the results of plugin tests. ## Synopsis Results is a structured way of storing results from plugins across a session, allowing those results to be retrieved later or by other plugins. Results objects are present on every Haraka connection _and_ transaction. When in a SMTP transaction, results from _both_ are applicable to that transaction. ## Usage Use results in your plugins like so: ```js exports.my_first_hook = function (next, connection) { // run a test ...... // store the results connection.results.add(this, {pass: 'my great test' }) // run another test ..... // store the results connection.results.add(this, {fail: 'gotcha!', msg: 'show this'}) } ``` Store the results in the transaction (vs connection): ```js connection.transaction.results.add(this, {...});` ``` ### Config options Each plugin can have custom settings in results.ini to control results logging. There are three options available: hide, order, and debug. - hide - a comma separated list of results to hide from the output - order - a comman separated list, specifing the order of items in the output - debug - log debug messages every time results are called ```ini ; put this in config/results.ini [plugin_name] hide=skip order=msg,pass,fail debug=0 ``` ### Results Functions #### add Store information. Most calls to `results` will append data to the lists in the connection. The following lists are available: pass - names of tests that passed fail - names of tests that failed skip - names of tests that were skipped (with a why, if you wish) err - error messages encountered during processing msg - arbitratry messages human - a custom summary to return (bypass collate) emit - log an INFO summary When err results are received, a logerror is automatically emitted, saving the need to specify {emit: true} with the request. Examples: ```js const results = connection.results results.add(this, {pass: 'null_sender'}) results.add(this, {fail: 'single_recipient'}) results.add(this, {skip: 'valid_bounce'} results.add(this, {err: 'timed out looking in couch cushions'}) results.add(this, {msg: 'I found a nickel!', emit: true}) ``` In addition to appending values to the predefined lists, arbitrary results can be stored in the cache: ```js results.add(this, { my_result: 'anything I want' }) ``` When arbirary values are stored, they are listed first in the log output. Their display can be suppressed with the **hide** option in results.ini. #### incr Increment counters. The argument to incr is an object with counter names and increment values. Examples: ```js results.incr(this, { unrecognized_commands: 1 }) results.incr(this, { karma: -1 }) results.incr(this, { karma: 2 }) ``` #### push Append items onto arrays. The argument to push is an object with array names and the new value to be appended to the array. Examples: ```js results.push(this, { dns_recs: 'name1' }) results.push(this, { dns_recs: 'name2' }) ``` #### collate ```js const summary = results.collate(this) ``` Formats the contents of the result cache and returns them. This function is called internally by `add()` after each update. #### get Retrieve the stored results as an object. The only argument is the name of the plugin whose results are desired. ```js const geoip = results.get('geoip') if (geoip && geoip.distance && geoip.distance > 2000) { .... } ``` Keep in mind that plugins also store results in the transaction. Example: ```js const sa = connection.transaction.results.get('spamassassin') if (sa && sa.score > 5) { .... } ``` #### has Check result contents for string or pattern matches. Syntax: ```js results.has('plugin_name', 'result_name', 'search_term') ``` - result_name: the name of an array or string in the result object - search_term: a string or RegExp object ### More Examples #### Store Results: ```js results.add(this, { pass: 'some_test' }) results.add(this, { pass: 'some_test(with reason)' }) ``` #### Retrieve exact match with **get**: ```js if (results.get('plugin_name').pass.indexOf('some_test') !== -1) { // some_test passed (1x) } ``` #### Retrieve a string match with **has** ```js if (results.has('plugin_name', 'pass', 'some_test')) { // some_test passed (1x) } ``` The syntax for using **has** is a little more pleasant. Both options require one to check for each reason which is unpleasant when and all we really want to know is if some_test passed or not. #### Retrieve a matching pattern: ```js if (results.has('plugin_name', 'pass', /^some_test/)) { // some_test passed (2x) } ``` ### Private Results To store structured data in results that are hidden from the human and human_html output, prefix the name of the key with an underscore. Example: ```js results.add(this, { _hidden: 'some data' }) ``` ## Redis Pub/Sub If a redis client is found on server.notes.redis, then new results are JSON encoded and published to Redis on the channel named `result-${UUID}`. This feature can be disabled by setting `[main]redis_publish=false` in results.ini. Plugins can recieve the events by psubscribing (pattern subscribe) to the channel named `result-${UUID}*` where ${UUID} is the connection UUID. This is from the karma plugin subscribing on the `connect_init` hook: ```js exports.register = function (next, server) { this.inherits('redis') register_hook('connect_init', 'redis_subscribe') register_hook('disconnect', 'redis_unsubscribe') } exports.redis_subscribe = function (next, connection) { this.redis_subscribe(connection, function () { connection.notes.redis.on('pmessage', (pattern, channel, message) => { // do stuff with messages that look like this // {"plugin":"karma","result":{"fail":"spamassassin.hits"}} // {"plugin":"geoip","result":{"country":"CN"}} }) next() }) } exports.redis_unsubscribe = function (next, connection) { this.redis_unsubscribe(connection) } ``` [ci-img]: https://github.com/haraka/haraka-results/actions/workflows/ci.yml/badge.svg [ci-url]: https://github.com/haraka/haraka-results/actions/workflows/ci.yml [cov-img]: https://codecov.io/github/haraka/haraka-results/coverage.svg [cov-url]: https://codecov.io/github/haraka/haraka-results [clim-img]: https://codeclimate.com/github/haraka/haraka-results/badges/gpa.svg [clim-url]: https://codeclimate.com/github/haraka/haraka-results