An object that's available in all action handlers and routers as req.hull.
It's a set of parameters and modules to work in the context of current organization and connector instance.
Cache available as req.hull.cache object. This class is being intiated and added to Context Object by QueueAgent.
If you want to customize cache behavior (for example ttl, storage etc.) please @see Infra.QueueAgent
Hull client calls which fetch ship settings could be wrapped with this method to cache the results
Parameters
Returns Promise
Saves ship data to the cache
Parameters
Returns Promise
Returns cached information
Parameters
keystring
Returns Promise
Clears the ship cache. Since Redis stores doesn't return promise for this method, it passes a callback to get a Promise
Parameters
keystring
Returns any Promise
Metric agent available as req.hull.metric object.
This class is being initiated by InstrumentationAgent.
If you want to change or override metrics behavior please @see Infra.InstrumentationAgent
Examples
req.hull.metric.value("metricName", metricValue = 1);
req.hull.metric.increment("metricName", incrementValue = 1); // increments the metric value
req.hull.metric.event("eventName", { text = "", properties = {} });Sets metric value for gauge metric
Parameters
metricstring metric namevaluenumber metric value (optional, default1)additionalTagsArray additional tags in form of["tag_name:tag_value"](optional, default[])
Returns mixed
Increments value of selected metric
Parameters
metricstring metric metric namevaluenumber value which we should increment metric by (optional, default1)additionalTagsArray additional tags in form of["tag_name:tag_value"](optional, default[])
Returns mixed
Parameters
Returns mixed
Parameters
queueAdapterObject adapter to run - when using this function in Context this param is boundctxContext Hull Context Object - when using this function in Context this param is boundjobNamestring name of specific job to executejobPayloadObject the payload of the joboptionsObject (optional, default{})options.ttlnumber? job producers can set an expiry value for the time their job can live in active state, so that if workers didn't reply in timely fashion, Kue will fail it with TTL exceeded error message preventing that job from being stuck in active state and spoiling concurrency.options.delaynumber? delayed jobs may be scheduled to be queued for an arbitrary distance in time by invoking the .delay(ms) method, passing the number of milliseconds relative to now. Alternatively, you can pass a JavaScript Date object with a specific time in the future. This automatically flags the Job as "delayed".options.queueNamestring? when you start worker with a different queue name, you can explicitly set it here to queue specific jobs to that queueoptions.priority(number | string)? you can use this param to specify priority of job
Examples
// app is Hull.Connector wrapped expressjs app
app.get((req, res) => {
req.hull.enqueue("jobName", { payload: "to-work" })
.then(() => {
res.end("ok");
});
});Returns Promise which is resolved when job is successfully enqueued
Meta
- deprecated: internal connector queue is considered an antipattern, this function is kept only for backward compatiblity
Parameters
dependenciesObjectoptionsObject (optional, default{})options.hostSecretstring? secret to sign req.hull.tokenoptions.port(Number | string)? port on which expressjs application should be startedoptions.clientConfigObject? additionalHullClientconfiguration (optional, default{})options.instrumentationObject? override default InstrumentationAgentoptions.cacheObject? override default CacheAgentoptions.queueObject? override default QueueAgentoptions.connectorNamestring? force connector name - if not provided will be taken from manifest.jsonoptions.skipSignatureValidationboolean? skip signature validation on notifications (for testing only)options.timeout(number | string)? global HTTP server timeout - format is parsed bymsnpm packageoptions.notificationValidatorHttpClient
This method applies all features of Hull.Connector to the provided application:
- serving
/manifest.json,/readmeand/endpoints - serving static assets from
/distand/assetsdirectiories - rendering
/views/*.htmlfiles withejsrenderer - timeouting all requests after 25 seconds
- adding Newrelic and Sentry instrumentation
- initiating the wole Context Object
- handling the
hullTokenparameter in a default way
Parameters
appexpress expressjs application
Returns express expressjs application
This is a supplement method which calls app.listen internally and also terminates instrumentation of the application calls.
If any error is not caught on handler level it will first go through instrumentation handler reporting it to sentry
and then a 500 Unhandled Error response will be send back to the client.
The response can be provided by the handler before passing it here.
Parameters
appexpress expressjs application
Returns http.Server
General utilities
Extends TransientError
This is an error related to wrong connector configuration. It's a transient error, but it makes sense to retry the payload only after the connector settings update.
Parameters
Extends Error
This is an error which should be handled by the connector implementation itself.
Rejecting or throwing this error without try/catch block will be treated as unhandled error.
Parameters
Examples
function validationFunction() {
throw new LogicError("Validation error", { action: "validation", payload: });
}Extends TransientError
This is a subclass of TransientError. It have similar nature but it's very common during connector operations so it's treated in a separate class. Usually connector is able to tell more about when exactly the rate limit error will be gone to optimize retry strategy.
Parameters
Extends TransientError
This error means that 3rd party API resources is out of sync comparing to Hull organization state. For example customer by accident removed a resource which we use to express segment information (for example user tags, user sub lists etc.) So this is a TransientError which could be retried after forcing "reconciliation" operation (which should recreate missing resource)
Parameters
Extends Error
This is a transient error related to either connectivity issues or temporary 3rd party API unavailability.
When using superagentErrorPlugin it's returned by some errors out-of-the-box.
Parameters
General utilities
OAuthHandler is a packaged authentication handler using Passport. You give it the right parameters, it handles the entire auth scenario for you.
It exposes hooks to check if the ship is Set up correctly, inject additional parameters during login, and save the returned settings during callback.
To make it working in Hull dashboard set following line in manifest.json file:
{
"admin": "/auth/"
}For example of the notifications payload see details
Parameters
optionsObjectoptions.namestring The name displayed to the User in the various screens.options.tokenInUrlboolean Some services (like Stripe) require an exact URL match. Some others (like Hubspot) don't pass the state back on the other hand. Setting this flag to false (default: true) removes thetokenQuerystring parameter in the URL to only rely on thestateparam.options.isSetupFunction A method returning a Promise, resolved if the ship is correctly setup, or rejected if it needs to display the Login screen. Lets you define in the Ship the name of the parameters you need to check for. You can return parameters in the Promise resolve and reject methods, that will be passed to the view. This lets you display status and show buttons and more to the customeroptions.onAuthorizeFunction A method returning a Promise, resolved when complete. Best used to save tokens and continue the sequence once saved.options.onLoginFunction A method returning a Promise, resolved when ready. Best used to process form parameters, and place them inreq.authParamsto be submitted to the Login sequence. Useful to add strategy-specific parameters, such as a portal ID for Hubspot for instance.options.StrategyFunction A Passport Strategy.options.viewsObject Required, A hash of view files for the different screens: login, home, failure, successoptions.optionsObject Hash passed to Passport to configure the OAuth Strategy. (See Passport OAuth Configuration)
Examples
const { oAuthHandler } = require("hull/lib/utils");
const { Strategy as HubspotStrategy } = require("passport-hubspot");
const app = express();
app.use(
'/auth',
oAuthHandler({
name: 'Hubspot',
tokenInUrl: true,
Strategy: HubspotStrategy,
options: {
clientID: 'xxxxxxxxx',
clientSecret: 'xxxxxxxxx', //Client Secret
scope: ['offline', 'contacts-rw', 'events-rw'] //App Scope
},
isSetup(req) {
if (!!req.query.reset) return Promise.reject();
const { token } = req.hull.ship.private_settings || {};
return !!token
? Promise.resolve({ valid: true, total: 2 })
: Promise.reject({ valid: false, total: 0 });
},
onLogin: req => {
req.authParams = { ...req.body, ...req.query };
return req.hull.client.updateSettings({
portalId: req.authParams.portalId
});
},
onAuthorize: req => {
const { refreshToken, accessToken } = req.account || {};
return req.hull.client.updateSettings({
refresh_token: refreshToken,
token: accessToken
});
},
views: {
login: 'login.html',
home: 'home.html',
failure: 'failure.html',
success: 'success.html'
}
})
);
//each view will receive the following data:
{
name: "The name passed as handler",
urls: {
login: '/auth/login',
success: '/auth/success',
failure: '/auth/failure',
home: '/auth/home',
},
ship: ship //The entire Ship instance's config
}Returns Function OAuth handler to use with expressjs
This is a method to request an extract of user base to be sent back to the Connector to a selected path which should be handled by notifHandler.
Parameters
optionsObject ={}
Examples
req.hull.helpers.requestExtract({ segment = null, path, fields = [], additionalQuery = {} });Returns Promise
Helper function to handle JSON extract sent to batch endpoint
Parameters
ctxObject Hull request contextoptionsObjectoptions.bodyObject request body object (req.body)options.batchSizeObject size of the chunk we want to pass to handleroptions.callbackFunction callback returning a Promise (will be called with array of elements)options.onResponseFunction callback called on successful inital responseoptions.onErrorFunction callback called during error
Returns Promise
Allows to update selected settings of the ship private_settings object. This is a wrapper over hullClient.utils.settings.update() call. On top of that it makes sure that the current context ship object is updated, and the ship cache is refreshed.
It will emit ship:update notify event.
Parameters
Examples
req.hull.helpers.updateSettings({ newSettings });Returns Promise
This is a general error handling SuperAgent plugin.
It changes default superagent retry strategy to rerun the query only on transient
connectivity issues (ECONNRESET, ETIMEDOUT, EADDRINFO, ESOCKETTIMEDOUT, ECONNABORTED).
So any of those errors will be retried according to retries option (defaults to 2).
If the retry fails again due to any of those errors the SuperAgent Promise will be rejected with special error class TransientError to distinguish between logical errors and flaky connection issues.
In case of any other request the plugin applies simple error handling strategy:
every non 2xx or 3xx response is treated as an error and the promise will be rejected.
Every connector ServiceClient should apply it's own error handling strategy by overriding ok handler.
Parameters
optionsObject (optional, default{})
Examples
superagent.get("http://test/test")
.use(superagentErrorPlugin())
.ok((res) => {
if (res.status === 401) {
throw new ConfigurationError();
}
if (res.status === 429) {
throw new RateLimitError();
}
return true;
})
.catch((error) => {
// error.constructor.name can be ConfigurationError, RateLimitError coming from `ok` handler above
// or TransientError coming from logic applied by `superagentErrorPlugin`
})Returns Function function to use as superagent plugin
This plugin takes client.logger and metric params from the Context Object and logs following log line:
ship.service_api.requestwith params:url- the original url passed to agent (use withsuperagentUrlTemplatePlugin)responseTime- full response time in msmethod- HTTP verbstatus- response status codevars- when usingsuperagentUrlTemplatePluginit will contain all provided variables
The plugin also issue a metric with the same name ship.service_api.request.
Parameters
optionsObject
Examples
const superagent = require('superagent');
const { superagentInstrumentationPlugin } = require('hull/lib/utils');
// const ctx is a Context Object here
const agent = superagent
.agent()
.use(
urlTemplatePlugin({
defaultVariable: 'mainVariable'
})
)
.use(
superagentInstrumentationPlugin({
logger: ctx.client.logger,
metric: ctx.metric
})
);
agent
.get('https://api.url/{{defaultVariable}}/resource/{{resourceId}}')
.tmplVar({
resourceId: 123
})
.then(res => {
assert(res.request.url === 'https://api.url/mainVariable/resource/123');
});
> Above code will produce following log line:
```sh
connector.service_api.call {
responseTime: 880.502444,
method: 'GET',
url: 'https://api.url/{{defaultVariable}}/resource/{{resourceId}}',
status: 200
}
```
> and following metrics:
```javascript
- `ship.service_api.call` - should be migrated to `connector.service_api.call`
- `connector.service_api.responseTime`
```Returns Function function to use as superagent plugin
This plugin allows to pass generic url with variables - this allows better instrumentation and logging on the same REST API endpoint when resource ids varies.
Parameters
defaultsObject default template variable
Examples
const superagent = require('superagent');
const { superagentUrlTemplatePlugin } = require('hull/lib/utils');
const agent = superagent.agent().use(
urlTemplatePlugin({
defaultVariable: 'mainVariable'
})
);
agent
.get('https://api.url/{{defaultVariable}}/resource/{{resourceId}}')
.tmplVar({
resourceId: 123
})
.then(res => {
assert(res.request.url === 'https://api.url/mainVariable/resource/123');
});Returns Function function to use as superagent plugin
Production ready connectors need some infrastructure modules to support their operation, allow instrumentation, queueing and caching. The Hull.Connector comes with default settings, but also allows to initiate them and set a custom configuration:
Examples
const instrumentation = new Instrumentation();
const cache = new Cache();
const queue = new Queue();
const connector = new Hull.Connector({ instrumentation, cache, queue });This is a wrapper over https://github.com/BryanDonovan/node-cache-manager to manage ship cache storage. It is responsible for handling cache key for every ship.
By default it comes with the basic in-memory store, but in case of distributed connectors being run in multiple processes for reliable operation a shared cache solution should be used. The Cache module internally uses node-cache-manager, so any of it's compatibile store like redis or memcache could be used:
The cache instance also exposes contextMiddleware whch adds req.hull.cache to store the ship and segments information in the cache to not fetch it for every request. The req.hull.cache is automatically picked and used by the Hull.Middleware and segmentsMiddleware.
The
req.hull.cachecan be used by the connector developer for any other caching purposes:
ctx.cache.get('object_name');
ctx.cache.set('object_name', object_value);
ctx.cache.wrap('object_name', () => {
return Promise.resolve(object_value);
});There are two
object nameswhich are reserved and cannot be used here:
- any ship id
- "segments"
IMPORTANT internal caching of
ctx.shipobject is refreshed onship:updatenotifications, if the connector doesn't subscribe for notification at all the cache won't be refreshed automatically. In such case disable caching, set short TTL or addnotifHandler
Parameters
optionsObject passed to node-cache-manager (optional, default{})
Examples
const redisStore = require("cache-manager-redis");
const { Cache } = require("hull/lib/infra");
const cache = new Cache({
store: redisStore,
url: 'redis://:XXXX@localhost:6379/0?ttl=600'
});
const connector = new Hull.Connector({ cache });It automatically sends data to DataDog, Sentry and Newrelic if appropriate ENV VARS are set:
- NEW_RELIC_LICENSE_KEY
- DATADOG_API_KEY
- SENTRY_URL
It also exposes the contextMiddleware which adds req.hull.metric agent to add custom metrics to the ship. Right now it doesn't take any custom options, but it's showed here for the sake of completeness.
Parameters
options(optional, default{})
Examples
const { Instrumentation } = require("hull/lib/infra");
const instrumentation = new Instrumentation();
const connector = new Connector.App({ instrumentation });By default it's initiated inside Hull.Connector as a very simplistic in-memory queue, but in case of production grade needs, it comes with a Kue or Bull adapters which you can initiate in a following way:
Options from the constructor of the BullAdapter or KueAdapter are passed directly to the internal init method and can be set with following parameters:
https://github.com/Automattic/kue#redis-connection-settings https://github.com/OptimalBits/bull/blob/master/REFERENCE.md#queue
The queue instance has a contextMiddleware method which adds req.hull.enqueue method to queue jobs - this is done automatically by Hull.Connector().setupApp(app):
req.hull.enqueue((jobName = ''), (jobPayload = {}), (options = {}));By default the job will be retried 3 times and the payload would be removed from queue after successfull completion.
Then the handlers to work on a specific jobs is defined in following way:
connector.worker({
jobsName: (ctx, jobPayload) => {
// process Payload
// this === job (kue job object)
// return Promise
}
});
connector.startWorker();Parameters
adapterObject
Examples
```javascript
const { Queue } = require("hull/lib/infra");
const BullAdapter = require("hull/lib/infra/queue/adapter/bull"); // or KueAdapter
const queueAdapter = new BullAdapter(options);
const queue = new Queue(queueAdapter);
const connector = new Hull.Connector({ queue });
```Meta
- deprecated: internal connector queue is considered an antipattern, this class is kept only for backward compatiblity