Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

taskcluster-client

Package Overview
Dependencies
Maintainers
2
Versions
433
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

taskcluster-client - npm Package Compare versions

Comparing version 0.5.1 to 0.6.0

listener.js

247

apis.json

@@ -186,3 +186,250 @@ {

}
},
"QueueEvents": {
"referenceUrl": "http://references.taskcluster.net/queue/v1/exchanges.json",
"reference": {
"version": "0.2.0",
"title": "Queue AMQP Exchanges",
"description": "The queue, typically available at `queue.taskcluster.net`, is responsible\nfor accepting tasks and track their state as they are executed by\nworkers. In order ensure they are eventually resolved.\n\nThis document describes AMQP exchanges offered by the queue, which allows\nthird-party listeners to monitor tasks as they progress to resolution.\nThese exchanges targets the following audience:\n * Schedulers, who takes action after tasks are completed,\n * Workers, who wants to listen for new or canceled tasks (optional),\n * Tools, that wants to update their view as task progress.\n\nYou'll notice that all the exchanges in the document shares the same\nrouting key pattern. This makes it very easy to bind to all message about\na certain kind tasks. You should also note that the `routing` property\nof a task will be used in the routing key. This property is user-defined\nand may contain dots (ie. multiple routing words).\n\n**Remark**, if the task-graph scheduler, documented elsewhere, is used to\nscheduler a task-graph, then task submitted will have their `routing` key\nprefixed by `task-graph-scheduler.<taskGraphId>.` this means that the\nfirst two words of the task `routing` key will be\n`'task-graph-scheduler'` and `taskGraphId`. This is useful if you're\ninterested in updates about a specific task-graph, and it is necessary\nto know if you're binding to the `task.routing` key and submitting tasks\nthrough the task-graph scheduler. See documentation for task-graph\nscheduler for more details.",
"exchangePrefix": "queue/v1/",
"entries": [
{
"type": "topic-exchange",
"exchange": "task-pending",
"name": "taskPending",
"title": "Task Pending Messages",
"description": "When a task becomes `pending` a message is posted to this exchange.\n\nThis is useful for workers who doesn't want to constantly poll the queue\nfor new tasks. The queue will also be authority for task states and\nclaims. But using this exchange workers should be able to distribute work\nefficiently and they would be able to reduce their polling interval\nsignificantly without affecting general responsiveness.",
"routingKey": [
{
"name": "taskId",
"summary": "`taskId` for the task this message concerns",
"multipleWords": false,
"required": true,
"maxSize": 22
},
{
"name": "runId",
"summary": "`runId` of latest run for the task, `_` if no run is exists for the task.",
"multipleWords": false,
"required": false,
"maxSize": 3
},
{
"name": "workerGroup",
"summary": "`workerGroup` of latest run for the task, `_` if no run is exists for the task.",
"multipleWords": false,
"required": false,
"maxSize": 22
},
{
"name": "workerId",
"summary": "`workerId` of latest run for the task, `_` if no run is exists for the task.",
"multipleWords": false,
"required": false,
"maxSize": 22
},
{
"name": "provisionerId",
"summary": "`provisionerId` this task is targeted at.",
"multipleWords": false,
"required": true,
"maxSize": 22
},
{
"name": "workerType",
"summary": "`workerType` this task must run on.",
"multipleWords": false,
"required": true,
"maxSize": 22
},
{
"name": "routing",
"summary": "task-specific routing key (`task.routing`).",
"multipleWords": true,
"required": true,
"maxSize": 128
}
],
"schema": "http://schemas.taskcluster.net/queue/v1/task-pending-message.json#"
},
{
"type": "topic-exchange",
"exchange": "task-running",
"name": "taskRunning",
"title": "Task Running Messages",
"description": "Whenever a task is claimed by a worker, a run is started on the worker,\nand a message is posted on this exchange.\n\n**Notice**, that the `logsUrl` may return `404` during the run, but by\nthe end of the run the `logsUrl` will be valid. But this may not have\nhappened when this message is posted.\n\nThe idea is that workers can choose to upload the `logs.json` file as the\nfirst thing they do, in which case it'll often be available after a few\nminutes. This is useful if the worker supports live logging.",
"routingKey": [
{
"name": "taskId",
"summary": "`taskId` for the task this message concerns",
"multipleWords": false,
"required": true,
"maxSize": 22
},
{
"name": "runId",
"summary": "`runId` of latest run for the task, `_` if no run is exists for the task.",
"multipleWords": false,
"required": false,
"maxSize": 3
},
{
"name": "workerGroup",
"summary": "`workerGroup` of latest run for the task, `_` if no run is exists for the task.",
"multipleWords": false,
"required": false,
"maxSize": 22
},
{
"name": "workerId",
"summary": "`workerId` of latest run for the task, `_` if no run is exists for the task.",
"multipleWords": false,
"required": false,
"maxSize": 22
},
{
"name": "provisionerId",
"summary": "`provisionerId` this task is targeted at.",
"multipleWords": false,
"required": true,
"maxSize": 22
},
{
"name": "workerType",
"summary": "`workerType` this task must run on.",
"multipleWords": false,
"required": true,
"maxSize": 22
},
{
"name": "routing",
"summary": "task-specific routing key (`task.routing`).",
"multipleWords": true,
"required": true,
"maxSize": 128
}
],
"schema": "http://schemas.taskcluster.net/queue/v1/task-running-message.json#"
},
{
"type": "topic-exchange",
"exchange": "task-completed",
"name": "taskCompleted",
"title": "Task Completed Messages",
"description": "When a task is completed by a worker a message is posted this exchange.\nThis message is routed using the `run-id`, `worker-group` and `worker-id`\nthat completed the task. But information about additional runs is also\navailable from the task status structure.\n\nUpon task completion a result structure is made available, you'll find\nthe url in the `resultURL` property. See _task storage_ documentation for\ndetails on the format of the file available through `resultUrl`.",
"routingKey": [
{
"name": "taskId",
"summary": "`taskId` for the task this message concerns",
"multipleWords": false,
"required": true,
"maxSize": 22
},
{
"name": "runId",
"summary": "`runId` of latest run for the task, `_` if no run is exists for the task.",
"multipleWords": false,
"required": false,
"maxSize": 3
},
{
"name": "workerGroup",
"summary": "`workerGroup` of latest run for the task, `_` if no run is exists for the task.",
"multipleWords": false,
"required": false,
"maxSize": 22
},
{
"name": "workerId",
"summary": "`workerId` of latest run for the task, `_` if no run is exists for the task.",
"multipleWords": false,
"required": false,
"maxSize": 22
},
{
"name": "provisionerId",
"summary": "`provisionerId` this task is targeted at.",
"multipleWords": false,
"required": true,
"maxSize": 22
},
{
"name": "workerType",
"summary": "`workerType` this task must run on.",
"multipleWords": false,
"required": true,
"maxSize": 22
},
{
"name": "routing",
"summary": "task-specific routing key (`task.routing`).",
"multipleWords": true,
"required": true,
"maxSize": 128
}
],
"schema": "http://schemas.taskcluster.net/queue/v1/task-completed-message.json#"
},
{
"type": "topic-exchange",
"exchange": "task-failed",
"name": "taskFailed",
"title": "Task Failed Messages",
"description": "Whenever a task is concluded to be failed a message is posted to this\nexchange. This happens if the task isn't completed before its `deadlìne`,\nall retries failed (i.e. workers stopped responding) or the task was\ncanceled by another entity.\n\nThe specific _reason_ is evident from that task status structure, refer\nto the `reason` property.",
"routingKey": [
{
"name": "taskId",
"summary": "`taskId` for the task this message concerns",
"multipleWords": false,
"required": true,
"maxSize": 22
},
{
"name": "runId",
"summary": "`runId` of latest run for the task, `_` if no run is exists for the task.",
"multipleWords": false,
"required": false,
"maxSize": 3
},
{
"name": "workerGroup",
"summary": "`workerGroup` of latest run for the task, `_` if no run is exists for the task.",
"multipleWords": false,
"required": false,
"maxSize": 22
},
{
"name": "workerId",
"summary": "`workerId` of latest run for the task, `_` if no run is exists for the task.",
"multipleWords": false,
"required": false,
"maxSize": 22
},
{
"name": "provisionerId",
"summary": "`provisionerId` this task is targeted at.",
"multipleWords": false,
"required": true,
"maxSize": 22
},
{
"name": "workerType",
"summary": "`workerType` this task must run on.",
"multipleWords": false,
"required": true,
"maxSize": 22
},
{
"name": "routing",
"summary": "task-specific routing key (`task.routing`).",
"multipleWords": true,
"required": true,
"maxSize": 128
}
],
"schema": "http://schemas.taskcluster.net/queue/v1/task-failed-message.json#"
}
]
}
}
}

53

client.js

@@ -28,4 +28,7 @@ /* This Source Code Form is subject to the terms of the Mozilla Public

* }
* baseUrl: 'http://.../v1' // API baseUrl, default is taken from reference
* baseUrl: 'http://.../v1' // baseUrl for API requests
* exchangePrefix: 'queue/v1/' // exchangePrefix prefix
* }
*
* `baseUrl` and `exchangePrefix` defaults to values from reference.
*/

@@ -36,3 +39,4 @@ exports.createClient = function(reference) {

this._options = _.defaults(options || {}, {
baseUrl: reference.baseUrl
baseUrl: reference.baseUrl || '',
exchangePrefix: reference.exchangePrefix || ''
}, _defaultOptions);

@@ -111,2 +115,44 @@ };

// For each topic-exchange entry
reference.entries.filter(function(entry) {
return entry.type === 'topic-exchange';
}).forEach(function(entry) {
// Create function for routing-key pattern construction
Client.prototype[entry.name] = function(routingKeyPattern) {
if (typeof(routingKeyPattern) !== 'string') {
// Allow for empty routing key patterns
if (routingKeyPattern === undefined ||
routingKeyPattern === null) {
routingKeyPattern = {};
}
// Check that the routing key pattern is an object
assert(routingKeyPattern instanceof Object,
"routingKeyPattern must be an object");
// Construct routingkey pattern as string from reference
routingKeyPattern = entry.routingKey.map(function(key) {
var value = routingKeyPattern[key.name];
if (typeof(value) === 'string') {
assert(key.multipleWords || value.indexOf('.') === -1,
"routingKey pattern '" + value + "' for " + key.name +
" cannot contain dots as it does not hold multiple words");
return value;
} else {
assert(value === null || value === undefined,
"Value: '" + value + "' is not supported as routingKey "+
"pattern for " + key.name);
return key.multipleWords ? '#' : '*';
}
}).join('.');
}
// Return values necessary to bind with EventHandler
return {
exchange: this._options.exchangePrefix + entry.exchange,
routingKeyPattern: routingKeyPattern,
routingKeyReference: _.cloneDeep(entry.routingKey)
};
};
});
// Return client class

@@ -141,1 +187,4 @@ return Client;

};
// Export listener
exports.Listener = require('./listener');

8

package.json
{
"name": "taskcluster-client",
"version": "0.5.1",
"version": "0.6.0",
"author": "Jonas Finnemann Jensen <jopsen@gmail.com>",

@@ -8,3 +8,3 @@ "description": "Client for interfacing taskcluster components",

"scripts": {
"test": "mocha test/client_test.js"
"test": "./test/runtests.sh"
},

@@ -20,3 +20,5 @@ "repository": {

"superagent-promise": "0.1.0",
"superagent-hawk": "0.0.3"
"superagent-hawk": "0.0.3",
"amqplib": "0.2.0",
"slugid": "1.0.1"
},

@@ -23,0 +25,0 @@ "devDependencies": {

# TaskCluster Client [![Build Status](https://travis-ci.org/taskcluster/taskcluster-client.svg?branch=master)](https://travis-ci.org/taskcluster/taskcluster-client)
_A taskcluster client library for node.js._
## Usage
This client library is generated from the auto-generated API reference.

@@ -10,2 +9,8 @@ You can create a Client class from a JSON reference object at runtime using

## Calling API End-Points
To invoke an API end-point instantiate a taskcluster Client class, these are
classes can be created from a JSON reference object, but a number of them are
also built-in to this library. In the following example we instantiate an
instance of the `Queue` Client class and use to to create a task.
```js

@@ -33,2 +38,56 @@ var taskcluster = require('taskcluster-client');

## Listening for Events
Many TaskCluster components publishes messages about current events over AMQP.
The JSON reference object also contains meta-data about declared AMQP topic
exchanges and their routing key construction. This is designed to make it easy
to construct routing key patterns and parse routing keys from incoming messages.
The following example create a `listener` and instantiate an instance of
the Client class `QueueEvents` which we use to find the exchange and create
a routing pattern to listen for completion of a specific task. The
`taskCompleted` method will construct a routing key pattern by using `*` or `#`
for missing entries, pending on whether or not they are single word or
multi-key entries.
```js
var taskcluster = require('taskcluster-client');
// Create a listener (this creates a queue on AMQP)
var listener = new taskcluster.Listener({
connectionString: 'amqp://...'
});
// Instantiate the QueueEvents Client class
var queueEvents = new taskcluster.QueueEvents();
// Bind to task-completed events from queue that matches routing key pattern:
// '<myTaskId>.*.*.*.*.*.#'
listener.bind(queueEvents.taskCompleted({taskId: '<myTaskId>'}));
// Listen for messages
listener.on('message', function(message) {
message.exchange // Exchange from which message came
message.payload // Documented on docs.taskcluster.net
message.routingKey // Message routing key in string format
message.routing.taskId // Element from parsed routing key
message.routing.runId // ...
message.redelivered // True, if message has been nack'ed and requeued
return new Promise(...);
});
// Start listening for events
listener.connect().then(function() {
// Now listening
});
```
The listener creates a AMQP queue, on the server side and subscribes to messages
on the queue. It's possible to use named queues, see details below. For details
on routing key entries refer to documentation on
[docs.taskcluster.net](docs.taskcluster.net).
**Remark,** API end-points and AMQP exchanges are typically documented in
separate reference files. For this reason they also have separate Client
classes, even if they are from the same component.
## Documentation

@@ -73,2 +132,13 @@ The set of API entries listed below is generated from the builtin references.

### Exchanges in `taskcluster.QueueEvents`
```js
// Create QueueEvents client instance with default exchangePrefix:
// - queue/v1/
var queueEvents = new taskcluster.QueueEvents(options);
```
* `queueEvents.taskPending(routingKeyPattern) : binding-info`
* `queueEvents.taskRunning(routingKeyPattern) : binding-info`
* `queueEvents.taskCompleted(routingKeyPattern) : binding-info`
* `queueEvents.taskFailed(routingKeyPattern) : binding-info`
<!-- END OF GENERATED DOCS -->

@@ -87,5 +157,3 @@

// Instantiate an instance of MyClient
var myClient = new MyClient({
credentials: {...}
});
var myClient = new MyClient(options);

@@ -98,3 +166,8 @@ // Make a request with a method on myClient

## Configuring API BaseUrls
## Configuration of API Invocations
There is a number of configuration options for Client which affects invocation
of API end-points. These are useful if using a non-default server, for example
when setting up a staging area or testing locally.
### Configuring API BaseUrls
If you use the builtin API Client classes documented above you can configure

@@ -110,3 +183,3 @@ the `baseUrl` when creating an instance of the client. As illustrated below:

## Configuring Credentials
### Configuring Credentials
When creating an instance of a Client class the credentials can be provided

@@ -139,3 +212,3 @@ in options. For example:

## Delegated Authorization
### Delegated Authorization
If your client has the scope `auth:can-delegate` you can send requests with

@@ -168,2 +241,39 @@ a scope set different from the one you have. This is useful when the

## Configuration of Exchange Bindings
When a taskcluster Client class is instantiated the option `exchangePrefix` may
be given. This will replace the default `exchangePrefix`. This can be useful if
deploying a staging area or similar. See example below:
```js
// Instantiate the QueueEvents Client class
var queueEvents = new taskcluster.QueueEvents({
exchangePrefix: 'staging-queue/v1/'
});
// This listener will now bind to: staging-queue/v1/task-completed
listener.bind(queueEvents.taskCompleted({taskId: '<myTaskId>'}));
```
## Using the Listener
TODO:
```
var listener = new taskcluster.Listener({
prefetch: 5, // Number of tasks to process in parallel
connectionString: 'amqp://...', // AMQP connection string
// If no queue name is given, the queue is:
// exclusive, autodeleted and non-durable
// If a queue name is given, the queue is:
// durable, not auto-deleted and non-exclusive
queueName: 'my-queue', // Queue name, undefined if none
maxLength: 0, // Max allowed queue size
});
listener.connect().then(...); // Setup listener and start
listener.pause().then(...); // Pause retrieval of new messages
listener.resume().then(...); // Start getting new messages
listener.close(); // Disconnect from AMQP
```
## Updating Builtin APIs

@@ -170,0 +280,0 @@ When releasing a new version of the `taskcluster-client` library, we should

@@ -22,2 +22,7 @@ #!/usr/bin/env node

/** Find instance name by making first character lower-case */
var instanceName = function(name) {
return name[0].toLowerCase() + name.substr(1);
};
var apis = loadApis();

@@ -27,3 +32,11 @@

DOCS_START_MARKER
].concat(_.keys(apis).map(function(name) {
];
// Generate documentation for methods
docs = docs.concat(_.keys(apis).filter(function(name) {
// Find component that hold functions
return apis[name].reference.entries.some(function(entry) {
return entry.type === 'function';
});
}).map(function(name) {
var api = apis[name];

@@ -36,3 +49,3 @@ return [

"// - " + api.reference.baseUrl,
"var " + name.toLowerCase() + " = new taskcluster." + name + "(options);",
"var " + instanceName(name) + " = new taskcluster." + name + "(options);",
"```"

@@ -50,9 +63,37 @@ ].concat(api.reference.entries.filter(function(entry) {

}
return " * `" + name.toLowerCase() + "." + entry.name +
return " * `" + instanceName(name) + "." + entry.name +
"(" + args.join(', ') + ") : " + retval + "`";
})).join('\n');
}).concat([
}));
// Generate documentation for exchanges
docs = docs.concat(_.keys(apis).filter(function(name) {
// Find component that hold functions
return apis[name].reference.entries.some(function(entry) {
return entry.type === 'topic-exchange';
});
}).map(function(name) {
var api = apis[name];
return [
"",
"### Exchanges in `taskcluster." + name + "`",
"```js",
"// Create " + name + " client instance with default exchangePrefix:",
"// - " + api.reference.exchangePrefix,
"var " + instanceName(name) + " = new taskcluster." + name + "(options);",
"```"
].concat(api.reference.entries.filter(function(entry) {
return entry.type === 'topic-exchange';
}).map(function(entry) {
return " * `" + instanceName(name) + "." + entry.name +
"(routingKeyPattern) : binding-info`";
})).join('\n');
}));
docs = docs.concat([
"",
DOCS_END_MARKER
])).join('\n')
]).join('\n');

@@ -59,0 +100,0 @@ // Load readme

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc