@taskcluster/client 88.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +572 -0
- package/package.json +36 -0
- package/src/apis.js +5089 -0
- package/src/client.js +891 -0
- package/src/download.js +150 -0
- package/src/hashstream.js +37 -0
- package/src/index.js +21 -0
- package/src/parsetime.js +37 -0
- package/src/retry.js +39 -0
- package/src/upload.js +101 -0
- package/src/utils.js +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
# Taskcluster Client for JS
|
|
2
|
+
|
|
3
|
+
[](https://yarnpkg.com/en/package/@taskcluster/client)
|
|
4
|
+
[](http://mozilla.org/MPL/2.0)
|
|
5
|
+
|
|
6
|
+
**A Taskcluster client library for (server-side) JS.**
|
|
7
|
+
|
|
8
|
+
This library is a complete interface to Taskcluster in JavaScript. It provides
|
|
9
|
+
an asynchronous interface for all Taskcluster API methods. This library is
|
|
10
|
+
used within Taskcluster itself for inter-service communication.
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
For a general guide to using Taskcluster clients, see [Calling Taskcluster APIs](https://docs.taskcluster.net/docs/manual/using/api).
|
|
15
|
+
|
|
16
|
+
### Setup
|
|
17
|
+
|
|
18
|
+
Before calling an API end-point, you'll need to create a client instance.
|
|
19
|
+
There is a class for each service, e.g., `Queue` and `Auth`. Each takes the
|
|
20
|
+
same options, shown in the example below. Note that only `rootUrl` is
|
|
21
|
+
required, and it's unusual to configure any other options aside from
|
|
22
|
+
`credentials`.
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
import taskcluster from '@taskcluster/client';
|
|
26
|
+
|
|
27
|
+
// Instantiate the Queue Client class
|
|
28
|
+
const queue = new taskcluster.Queue({
|
|
29
|
+
// rootUrl for this Taskcluster instance (required)
|
|
30
|
+
rootUrl: 'https://taskcluster.myproject.org',
|
|
31
|
+
|
|
32
|
+
// Taskcluster credentials (required only for API methods that require scopes)
|
|
33
|
+
credentials: {
|
|
34
|
+
clientId: '...',
|
|
35
|
+
accessToken: '...',
|
|
36
|
+
// Certificate must also be provided if using temporary credentials,
|
|
37
|
+
// this can be either a JSON object or a JSON string.
|
|
38
|
+
certificate: {...} // Only applicable for temporary credentials
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// timeout for _each_ invidual http request
|
|
42
|
+
timeout: 30 * 1000,
|
|
43
|
+
|
|
44
|
+
// maximum number of retries for transient errors (default 5)
|
|
45
|
+
retries: 5,
|
|
46
|
+
|
|
47
|
+
// Multiplier for computation of retry delay: 2 ^ retry * delayFactor,
|
|
48
|
+
// 100 ms is solid for servers, and 500ms - 1s is suitable for background
|
|
49
|
+
// processes
|
|
50
|
+
delayFactor: 100,
|
|
51
|
+
|
|
52
|
+
// Randomization factor added as.
|
|
53
|
+
// delay = delay * random([1 - randomizationFactor; 1 + randomizationFactor])
|
|
54
|
+
randomizationFactor: 0.25,
|
|
55
|
+
|
|
56
|
+
// Maximum retry delay (defaults to 30 seconds)
|
|
57
|
+
maxDelay: 30 * 1000,
|
|
58
|
+
|
|
59
|
+
// By default we share a global HTTP agent. If you specify one, your instance
|
|
60
|
+
// will have its own agent with the given options...
|
|
61
|
+
agent: undefined,
|
|
62
|
+
|
|
63
|
+
// Fake methods, for testing (see below)
|
|
64
|
+
fake: null,
|
|
65
|
+
|
|
66
|
+
// authorized scopes for use in requests by this client
|
|
67
|
+
authorizedScopes: undefined,
|
|
68
|
+
|
|
69
|
+
// (optional) If set, this will be added to requests as a `x-taskcluster-trace-id` header
|
|
70
|
+
traceId: undefined
|
|
71
|
+
|
|
72
|
+
// (optional) This supports different ways of finding Taskcluster services. Currently only
|
|
73
|
+
// values are `default` and `k8s-dns`. The latter of which is for Taskcluster
|
|
74
|
+
// internal use only.
|
|
75
|
+
serviceDiscoveryScheme: 'default'
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
If you need to create a client similar to a existing client, but with some
|
|
80
|
+
options changed, use `client.use(options)`:
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
queue
|
|
84
|
+
.use({retries: 0}) // disable retries for this request
|
|
85
|
+
.createTask(..)
|
|
86
|
+
.then(..);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
This replaces any given options with new values. For `traceId` in particular, you can use
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
queue.taskclusterPerRequestInstance({traceId});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Which is a special interface mostly useful for Taskcluster internal use.
|
|
96
|
+
|
|
97
|
+
#### Authentication Options
|
|
98
|
+
|
|
99
|
+
You can automatically read credentials and rootUrl from the standard `TASKCLUSTER_…`
|
|
100
|
+
[environment
|
|
101
|
+
variables](https://docs.taskcluster.net/docs/manual/design/env-vars) with
|
|
102
|
+
`taskcluster.fromEnvVars()` with `fromEnvVars`:
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
const auth = new taskcluster.Auth({
|
|
106
|
+
...taskcluster.fromEnvVars(),
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Note that this function does not respect `TASKCLUSTER_PROXY_URL`. To use the Taskcluster Proxy from within a task:
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
const auth = new taskcluster.Auth({
|
|
114
|
+
rootUrl: process.env.TASKCLUSTER_PROXY_URL,
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
You may also provide credentials directly. For example:
|
|
119
|
+
```js
|
|
120
|
+
const auth = new taskcluster.Auth({
|
|
121
|
+
credentials: {
|
|
122
|
+
clientId: '...',
|
|
123
|
+
accessToken: '...'
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
If the `clientId` and `accessToken` are not given, no credentials will be used.
|
|
128
|
+
|
|
129
|
+
#### Global Configuration
|
|
130
|
+
|
|
131
|
+
You can set any of these values as global configuration options:
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
// Configure default options
|
|
135
|
+
taskcluster.config({
|
|
136
|
+
rootUrl: "https://somesite.com",
|
|
137
|
+
credentials: {
|
|
138
|
+
clientId: '...',
|
|
139
|
+
accessToken: '...'
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// No rootUrl needed here
|
|
144
|
+
const auth = new taskcluster.Auth();
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Authorized Scopes
|
|
148
|
+
|
|
149
|
+
If you wish to perform requests on behalf of a third-party that has small set
|
|
150
|
+
of scopes than you do. You can specify [which scopes your request should be
|
|
151
|
+
allowed to
|
|
152
|
+
use](https://docs.taskcluster.net/docs/manual/design/apis/hawk/authorized-scopes),
|
|
153
|
+
in the `authorizedScopes` option. See example below:
|
|
154
|
+
|
|
155
|
+
```js
|
|
156
|
+
// Create a Queue Client class can only define tasks for a specific workerType
|
|
157
|
+
const queue = new taskcluster.Queue({
|
|
158
|
+
// Credentials that can define tasks for any provisioner and workerType.
|
|
159
|
+
credentials: {
|
|
160
|
+
clientId: '...',
|
|
161
|
+
accessToken: '...'
|
|
162
|
+
},
|
|
163
|
+
// Restricting this instance of the Queue client to only one scope
|
|
164
|
+
authorizedScopes: ['queue:create-task:highest:my-provisioner/my-worker-type']
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// This request will only be successful, if the task posted is aimed at
|
|
168
|
+
// "my-worker-type/my-provisioner".
|
|
169
|
+
await queue.createTask(taskId taskDefinition).then(function(result) {
|
|
170
|
+
// ...
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Calling API Methods
|
|
175
|
+
|
|
176
|
+
Once you have a client object, calling API methods is as simple as invoking a
|
|
177
|
+
method on the object. All API methods are async, and their function signatures
|
|
178
|
+
match those in the reference documentation. In general, URL arguments are
|
|
179
|
+
positional JS arguments, and any request payload is provided in a JSON object
|
|
180
|
+
in the final argument.
|
|
181
|
+
|
|
182
|
+
Some API end-points may take query-string options. This is indicated in the
|
|
183
|
+
signature in the reference documentation as `[options]`. These options are
|
|
184
|
+
always _optional_, commonly used for continuation tokens when paging a list.
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
// Create task using the queue client
|
|
188
|
+
const taskId = '...';
|
|
189
|
+
const result = await queue.createTask(taskId, payload);
|
|
190
|
+
console.log(result.status);
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Generating URLs (Internal and External)
|
|
195
|
+
|
|
196
|
+
For the following section, there are 2 internal and 2 external functions. The
|
|
197
|
+
external functions should be used when a built url is leaving the deployment. One
|
|
198
|
+
example would be when it results in a redirect to an artifact for users. This distinction
|
|
199
|
+
is only important when using a non-default service discovery scheme; with the default
|
|
200
|
+
scheme, internal and external functions behave the same.
|
|
201
|
+
|
|
202
|
+
| | Unsigned | Signed |
|
|
203
|
+
| Internal | `buildUrl` | `buildSignedUrl` |
|
|
204
|
+
| External | `externalBuildUrl` | `externalBuildSignedUrl` |
|
|
205
|
+
|
|
206
|
+
You can build a URL for any API method, although this feature is
|
|
207
|
+
mostly useful for request that don't require any authentication. To construct a
|
|
208
|
+
url for a request use the `buildUrl`/`externalBuildUrl` method, as illustrated in the following
|
|
209
|
+
example:
|
|
210
|
+
|
|
211
|
+
```js
|
|
212
|
+
// Create queue instance
|
|
213
|
+
const queue = new taskcluster.Queue(...);
|
|
214
|
+
|
|
215
|
+
// Build url to get a specific task
|
|
216
|
+
const url = queue.buildUrl(
|
|
217
|
+
queue.getTask, // Method to build url for.
|
|
218
|
+
taskId // First parameter for the method, in this case taskId
|
|
219
|
+
);
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
It's possible to build signed URLs, including authentication information, for
|
|
223
|
+
all `GET` requests. A signed url contains a query-string parameter called
|
|
224
|
+
`bewit`, this parameter holds expiration time, signature and scope restrictions
|
|
225
|
+
(if applied). The signature covers the following parameters:
|
|
226
|
+
|
|
227
|
+
* Expiration time,
|
|
228
|
+
* Url and query-string, and
|
|
229
|
+
* scope restrictions (if applied)
|
|
230
|
+
|
|
231
|
+
These signed urls are very convenient if you want to grant somebody access to
|
|
232
|
+
specific resource without proxying the request or sharing your credentials.
|
|
233
|
+
For example it's fairly safe to provide someone with a signed url for a
|
|
234
|
+
specific artifact that is protected by a scope. See example below.
|
|
235
|
+
|
|
236
|
+
```js
|
|
237
|
+
// Create queue instance
|
|
238
|
+
const queue = new taskcluster.Queue(...);
|
|
239
|
+
|
|
240
|
+
// Build signed url
|
|
241
|
+
const signedUrl = queue.buildSignedUrl(
|
|
242
|
+
queue.getArtifactFromRun, // method to build signed url for.
|
|
243
|
+
taskId, // TaskId parameter
|
|
244
|
+
runId, // RunId parameter
|
|
245
|
+
artifactName, // Artifact name parameter
|
|
246
|
+
{
|
|
247
|
+
expiration: 60 * 10 // Expiration time in seconds
|
|
248
|
+
});
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Please, note that the `payload` parameter cannot be encoded in the signed url
|
|
252
|
+
and must be sent as request payload. This should work fine, just remember that
|
|
253
|
+
it's only possible to make signed urls for `GET` requests, which in most cases
|
|
254
|
+
don't take a payload.
|
|
255
|
+
|
|
256
|
+
Also please consider using a relatively limited expiration time, as it's not
|
|
257
|
+
possible to retract a signed url without revoking your credentials.
|
|
258
|
+
For more technical details on signed urls, see _bewit_ urls in
|
|
259
|
+
[mozilla/hawk](https://github.com/mozilla/hawk).
|
|
260
|
+
|
|
261
|
+
### Generating Temporary Credentials
|
|
262
|
+
|
|
263
|
+
If you have non-temporary taskcluster credentials you can generate a set of
|
|
264
|
+
[temporary credentials](https://docs.taskcluster.net/docs/manual/design/apis/hawk/temporary-credentials) as follows. Notice that the credentials cannot last more
|
|
265
|
+
than 31 days, and you can only revoke them by revoking the credentials that was
|
|
266
|
+
used to issue them (this takes up to one hour).
|
|
267
|
+
|
|
268
|
+
```js
|
|
269
|
+
const credentials = taskcluster.createTemporaryCredentials({
|
|
270
|
+
// Name of temporary credential (optional)
|
|
271
|
+
clientId: '...',
|
|
272
|
+
// Validity of temporary credentials starts here
|
|
273
|
+
start: new Date(),
|
|
274
|
+
// Expiration of temporary credentials
|
|
275
|
+
expiry: new Date(new Date().getTime() + 5 * 60 * 1000),
|
|
276
|
+
// Scopes to grant the temporary credentials
|
|
277
|
+
scopes: ['ScopeA', 'ScopeB', ...]
|
|
278
|
+
credentials: { // Non-temporary taskcluster credentials
|
|
279
|
+
clientId: '...'
|
|
280
|
+
accessToken: '...'
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
You cannot use temporary credentials to issue new temporary credentials. You
|
|
286
|
+
must have `auth:create-client:<name>` to create a named temporary credential,
|
|
287
|
+
but unnamed temporary credentials can be created regardless of your scopes.
|
|
288
|
+
|
|
289
|
+
### Handling Timestamps
|
|
290
|
+
|
|
291
|
+
A lot of taskcluster APIs requires ISO 8601 time stamps offset into the future
|
|
292
|
+
as way of providing expiration, deadlines, etc. These can be easily created
|
|
293
|
+
using `new Date().toJSON()`, however, it can be rather error prone and tedious
|
|
294
|
+
to offset `Date` objects into the future. Therefore this library comes with two
|
|
295
|
+
utility functions for this purposes.
|
|
296
|
+
|
|
297
|
+
```js
|
|
298
|
+
const dateObject = taskcluster.fromNow("2 days 3 hours 1 minute");
|
|
299
|
+
const dateString = taskcluster.fromNowJSON("2 days 3 hours 1 minute");
|
|
300
|
+
assert(dateObject.toJSON() === dateString);
|
|
301
|
+
// dateObject = now() + 2 days 2 hours and 1 minute
|
|
302
|
+
assert(new Date().getTime() < dateObject.getTime());
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
By default it will offset the date time into the future, if the offset strings
|
|
306
|
+
are prefixed minus (`-`) the date object will be offset into the past. This is
|
|
307
|
+
useful in some corner cases.
|
|
308
|
+
|
|
309
|
+
```js
|
|
310
|
+
const dateObject = taskcluster.fromNow("- 1 year 2 months 3 weeks 5 seconds");
|
|
311
|
+
// dateObject = now() - 1 year, 2 months, 3 weeks and 5 seconds
|
|
312
|
+
assert(new Date().getTime() > dateObject.getTime());
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
The offset string is ignorant of whitespace and case insensitive. It may also
|
|
316
|
+
optionally be prefixed plus `+` (if not prefixed minus), any `+` prefix will be
|
|
317
|
+
ignored. However, entries in the offset string must be given in order from
|
|
318
|
+
high to low, ie. `2 years 1 day`. Additionally, various shorthands may be
|
|
319
|
+
employed, as illustrated below.
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
years, year, yr, y
|
|
323
|
+
months, month, mo
|
|
324
|
+
weeks, week, wk, w
|
|
325
|
+
days, day, d
|
|
326
|
+
hours, hour, hr, h
|
|
327
|
+
minutes, minute, min
|
|
328
|
+
seconds, second, sec, s
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
The `fromNow` method may also be given a date to be relative to as a second
|
|
332
|
+
argument. This is useful if offset the task expiration relative to the the task
|
|
333
|
+
deadline or doing something similar.
|
|
334
|
+
|
|
335
|
+
```js
|
|
336
|
+
const dateObject1 = taskcluster.fromNow("2 days 3 hours");
|
|
337
|
+
// dateObject1 = now() + 2 days and 3 hours
|
|
338
|
+
const dateObject2 = taskcluster.fromNow("1 year", dateObject1);
|
|
339
|
+
// dateObject2 = now() + 1 year, 2 days and 3 hours
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Generating SlugIDs
|
|
343
|
+
|
|
344
|
+
In node you can rely on the `slugid` module to generate slugids, but we already
|
|
345
|
+
need it in `@taskcluster/client` and expose the preferred slugid generation
|
|
346
|
+
function as `taskcluster.slugid()`.
|
|
347
|
+
|
|
348
|
+
```js
|
|
349
|
+
import taskcluster from '@taskcluster/client';
|
|
350
|
+
|
|
351
|
+
// Generate new taskId
|
|
352
|
+
const taskId = taskcluster.slugid();
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
The generates _nice_ random slugids, refer to slugid module for further details.
|
|
356
|
+
|
|
357
|
+
### Uploading and Downloading
|
|
358
|
+
|
|
359
|
+
The Object service provides an API for reliable uploads and downloads of large objects.
|
|
360
|
+
These are most frequently used to store artifacts on behalf of the Queue service.
|
|
361
|
+
This library provides convenience methods to implement the client portion of those APIs, providing well-tested, resilient upload and download functionality.
|
|
362
|
+
These methods will negotiate the appropriate method with the object service and perform the required steps to transfer the data.
|
|
363
|
+
|
|
364
|
+
In either case, you will need to provide a configured `Object` instance with appropriate credentials for the operation.
|
|
365
|
+
You must also provide a `streamFactory` which, on each call, returns a Readable or Writable stream to handle the object data.
|
|
366
|
+
This function may be async (return a Promise).
|
|
367
|
+
In the event of retries, this function may be called several times, and should return a fresh stream on each invocation.
|
|
368
|
+
|
|
369
|
+
Both `upload` and `download` support the same retry configuration as clients, as described above, with the same defaults.
|
|
370
|
+
Note that these parameters apply only to the data-transfer portion of the process.
|
|
371
|
+
The calls to Object service endpoints will be governed by the retry configuration of the given `Object` instance.
|
|
372
|
+
|
|
373
|
+
For upload:
|
|
374
|
+
|
|
375
|
+
```javascript
|
|
376
|
+
await taskcluster.upload({
|
|
377
|
+
// paramters for the createObject endpoint
|
|
378
|
+
projectId,
|
|
379
|
+
name,
|
|
380
|
+
expires,
|
|
381
|
+
uploadId, // optional; will be generated randomly if omitted
|
|
382
|
+
|
|
383
|
+
// metadata about the data being uploaded
|
|
384
|
+
contentType,
|
|
385
|
+
contentLength,
|
|
386
|
+
|
|
387
|
+
// see above
|
|
388
|
+
object,
|
|
389
|
+
streamFactory,
|
|
390
|
+
retries.,
|
|
391
|
+
delayFactor.,
|
|
392
|
+
randomizationFactor.,
|
|
393
|
+
maxDelay.,
|
|
394
|
+
});
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
For downloading objects, returning the content type:
|
|
398
|
+
|
|
399
|
+
```javascript
|
|
400
|
+
let contentType = await taskcluster.download({
|
|
401
|
+
// the object to download
|
|
402
|
+
name,
|
|
403
|
+
|
|
404
|
+
// see above
|
|
405
|
+
object,
|
|
406
|
+
streamFactory,
|
|
407
|
+
retries.,
|
|
408
|
+
delayFactor.,
|
|
409
|
+
randomizationFactor.,
|
|
410
|
+
maxDelay.,
|
|
411
|
+
});
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
For example:
|
|
415
|
+
|
|
416
|
+
```javascript
|
|
417
|
+
const object = new taskcluster.Object(taskcluster.fromEnvVars());
|
|
418
|
+
const contentType = await taskcluster.download({
|
|
419
|
+
name: 'testing/data.tgz',
|
|
420
|
+
object,
|
|
421
|
+
streamFactory: () => fs.createWriteStream('data.tgz'),
|
|
422
|
+
});
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
And, wrapping that to support downloading artifacts:
|
|
426
|
+
```javascript
|
|
427
|
+
let contentType = await taskcluster.downloadArtifact({
|
|
428
|
+
// the artifact to download
|
|
429
|
+
taskId,
|
|
430
|
+
runId, // optional, defaulting to the latest run
|
|
431
|
+
name,
|
|
432
|
+
|
|
433
|
+
queue, // Queue instance with appropriate credentials to read the artifact
|
|
434
|
+
// (the queue will supply object-service credentials if necessary)
|
|
435
|
+
|
|
436
|
+
// see above
|
|
437
|
+
streamFactory,
|
|
438
|
+
retries.,
|
|
439
|
+
delayFactor.,
|
|
440
|
+
randomizationFactor.,
|
|
441
|
+
maxDelay.,
|
|
442
|
+
});
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Inspecting Credentials
|
|
446
|
+
|
|
447
|
+
Your users may find the options for Taskcluster credentials overwhelming. You
|
|
448
|
+
can help by interpreting the credentials for them.
|
|
449
|
+
|
|
450
|
+
The `credentialInformation(rootUrl, credentials)` function returns a promise
|
|
451
|
+
with information about the given credentials:
|
|
452
|
+
|
|
453
|
+
```js
|
|
454
|
+
{
|
|
455
|
+
clientId: "..", // name of the credential
|
|
456
|
+
type: "..", // type of credential, e.g., "temporary"
|
|
457
|
+
active: "..", // active (valid, not disabled, etc.)
|
|
458
|
+
start: "..", // validity start time (if applicable)
|
|
459
|
+
expiry: "..", // validity end time (if applicable)
|
|
460
|
+
scopes: ["..."], // associated scopes (if available)
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
The resulting information should *only* be used for presentation purposes, and
|
|
465
|
+
never for access control. This function may fail unexpectedly with invalid
|
|
466
|
+
credentials, and performs no cryptographic checks. It is acceptable to use the
|
|
467
|
+
scopes result to determine whether to display UI elements associated with a
|
|
468
|
+
particular scope, as long as the underlying API performs more reliable
|
|
469
|
+
authorization checks.
|
|
470
|
+
|
|
471
|
+
### Listening for Events
|
|
472
|
+
|
|
473
|
+
**NOTE** `PulseListener` is no longer included in `@taskcluster/client`;
|
|
474
|
+
instead, use `PulseConsumer` from
|
|
475
|
+
[@taskcluster/lib-pulse](../../libraries/pulse).
|
|
476
|
+
|
|
477
|
+
However, this library helpfully includes bindings for exchanges declared by
|
|
478
|
+
various Taskcluster services. To use these with `@taskcluster/lib-pulse`,
|
|
479
|
+
create an `..Events` instance, call the apprporiate methods on it to construct
|
|
480
|
+
a binding, and pass that to `pulse.consume`:
|
|
481
|
+
|
|
482
|
+
```js
|
|
483
|
+
import taskcluster from '@taskcluster/client';
|
|
484
|
+
|
|
485
|
+
// Instantiate the QueueEvents Client class
|
|
486
|
+
const queueEvents = new taskcluster.QueueEvents({rootUrl: ..});
|
|
487
|
+
|
|
488
|
+
let pc = await pulse.consume({
|
|
489
|
+
bindings: [
|
|
490
|
+
// Bind to task-completed events from queue that matches routing key pattern:
|
|
491
|
+
// 'primary.<myTaskId>.*.*.*.*.*.#'
|
|
492
|
+
queueEvents.taskCompleted({taskId: myTaskId});
|
|
493
|
+
], ..);
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
### Fake API Methods
|
|
497
|
+
|
|
498
|
+
In testing, it is useful to be able to "fake out" client methods so that they
|
|
499
|
+
do not try to communicate with an actual, external service. The normal client
|
|
500
|
+
argument checking still takes place, and a function of your design will be called
|
|
501
|
+
instead of calling the external service.
|
|
502
|
+
|
|
503
|
+
This is set up when constructing the client. Typically, this occurs in a
|
|
504
|
+
`@taskcluster/lib-loader` entry.
|
|
505
|
+
|
|
506
|
+
```javascript
|
|
507
|
+
setup(function () {
|
|
508
|
+
// inject the dependency with a stickyLoader from @taskcluster/lib-testing
|
|
509
|
+
helper.load.inject('secrets', new taskcluster.Secrets({
|
|
510
|
+
fake: {
|
|
511
|
+
get: (name) => 'my-hardcoded-secret',
|
|
512
|
+
},
|
|
513
|
+
});
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
test('test the thing', async function() {
|
|
517
|
+
// Get secrets from injection above
|
|
518
|
+
let secrets = await helper.load('secrets');
|
|
519
|
+
|
|
520
|
+
// Do something with the secrets object
|
|
521
|
+
let s = await secrets.get('thing-to-read');
|
|
522
|
+
assume(s).is.a('string');
|
|
523
|
+
|
|
524
|
+
// Make assertions over recorded calls
|
|
525
|
+
assume(secrets.fakeCalls.get).deep.contains({
|
|
526
|
+
name: 'thing-to-read',
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
try {
|
|
530
|
+
await secrets.remove('...', {}); // throws and error because we didn't fake it
|
|
531
|
+
} catch (err) {
|
|
532
|
+
// pass
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
```
|
|
536
|
+
### Creating Client Classes Dynamically
|
|
537
|
+
|
|
538
|
+
You can create a Client class from a reference JSON object as illustrated
|
|
539
|
+
below. This is unusual, as generally the latest version of the library
|
|
540
|
+
contains pre-defined classes for all Taskcluster services.
|
|
541
|
+
|
|
542
|
+
```js
|
|
543
|
+
const reference = {...}; // JSON from <rootUrl>/references/<serviceName>/<apiVersion>/api.json
|
|
544
|
+
|
|
545
|
+
// Create Client class
|
|
546
|
+
const MyClient = taskcluster.createClient(reference);
|
|
547
|
+
|
|
548
|
+
// Instantiate an instance of MyClient
|
|
549
|
+
const myClient = new MyClient(options);
|
|
550
|
+
|
|
551
|
+
// Make a request with a method on myClient
|
|
552
|
+
myClient.myMethod(arg1, arg2, payload).then(function(result) {
|
|
553
|
+
// ...
|
|
554
|
+
});
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### Internal Service Discovery
|
|
558
|
+
|
|
559
|
+
To allow for more efficient routing between Taskcluster services running alongside each other in
|
|
560
|
+
a Kubernetes cluster, this library has configurable support for using
|
|
561
|
+
[DNS for Services and Pods](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/). To
|
|
562
|
+
configure this on all clients created from this library, use `taskcluster.setServiceDiscoveryScheme('k8s-dns')`.
|
|
563
|
+
To configure this for an instantiation of a client class **or to override the setting back to default** you
|
|
564
|
+
can `new taskcluster.Auth({..., serviceDiscoveryScheme: 'k8s-dns'});`. The value for default behavior is `default`.
|
|
565
|
+
|
|
566
|
+
## Compatibility
|
|
567
|
+
|
|
568
|
+
This library is co-versioned with Taskcluster itself.
|
|
569
|
+
That is, a client with version x.y.z contains API methods corresponding to Taskcluster version x.y.z.
|
|
570
|
+
Taskcluster is careful to maintain API compatibility, and guarantees it within a major version.
|
|
571
|
+
That means that any client with version x.* will work against any Taskcluster services at version x.*, and is very likely to work for many other major versions of the Taskcluster services.
|
|
572
|
+
Any incompatibilities are noted in the [Changelog](https://github.com/taskcluster/taskcluster/blob/main/CHANGELOG.md).
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@taskcluster/client",
|
|
3
|
+
"version": "88.0.1",
|
|
4
|
+
"author": "Jonas Finnemann Jensen <jopsen@gmail.com>",
|
|
5
|
+
"description": "Client for interfacing taskcluster components",
|
|
6
|
+
"license": "MPL-2.0",
|
|
7
|
+
"repository": "https://github.com/taskcluster/taskcluster/tree/main/clients/client",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "mocha test/*_test.js",
|
|
10
|
+
"package-test": "sh package_test.sh"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"debug": "^4.1.1",
|
|
14
|
+
"got": "^14.4.2",
|
|
15
|
+
"hawk": "^9.0.1",
|
|
16
|
+
"lodash": "^4.17.4",
|
|
17
|
+
"slugid": "^5.0.1",
|
|
18
|
+
"taskcluster-lib-urls": "^13.0.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"http-proxy": "^1.18.0",
|
|
22
|
+
"mocha": "^11.7.1",
|
|
23
|
+
"nock": "^13.5.5"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": "22.17.1"
|
|
27
|
+
},
|
|
28
|
+
"type": "module",
|
|
29
|
+
"files": [
|
|
30
|
+
"src"
|
|
31
|
+
],
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"main": "./src/index.js"
|
|
36
|
+
}
|