1 | # OpenWhisk Client for JavaScript
|
2 |
|
3 | [![Build Status](https://travis-ci.org/apache/incubator-openwhisk-client-js.svg?branch=master)](https://travis-ci.org/apache/incubator-openwhisk-client-js)
|
4 | [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0)
|
5 | [![codecov](https://codecov.io/gh/apache/incubator-openwhisk-client-js/branch/master/graph/badge.svg)](https://codecov.io/gh/apache/incubator-openwhisk-client-js)
|
6 |
|
7 | JavaScript client library for the [Apache OpenWhisk](https://github.com/apache/incubator-openwhisk) platform.
|
8 | Provides a wrapper around the [OpenWhisk APIs](https://github.com/apache/incubator-openwhisk/blob/fb001afa237476eda0c0f6494ee92702e5986538/core/controller/src/main/resources/apiv1swagger.json) (Swagger JSON).
|
9 |
|
10 | ## installation
|
11 |
|
12 | ```bash
|
13 | $ npm install openwhisk
|
14 | ```
|
15 |
|
16 | ## usage
|
17 |
|
18 | ### within openwhisk platform
|
19 |
|
20 | This client library can use environment parameters to automatically configure the authentication credentials, platform endpoint and namespace. These parameters are defined within the Node.js runtime environment in OpenWhisk. Unless you want to override these values, you can construct the client instance without further configuration.
|
21 |
|
22 | ```javascript
|
23 | var openwhisk = require('openwhisk');
|
24 |
|
25 | function action() {
|
26 | var ow = openwhisk();
|
27 | return ow.actions.invoke('sample')
|
28 | }
|
29 |
|
30 | exports.main = action
|
31 | ```
|
32 |
|
33 | _All methods return a Promise resolved asynchronously with the results. Failures are available through the catch method._
|
34 |
|
35 | ```javascript
|
36 | ow.resource.operation().then(function () { // success! }).catch(function (err) { // failed! })
|
37 | ```
|
38 |
|
39 | Users can override default constructor parameters by passing in explicit options as shown in the example below.
|
40 |
|
41 | _**Please note**: Due to [an issue](https://github.com/openwhisk/openwhisk/issues/1751) with the Node.js runtime in OpenWhisk, environment variables used by the constructor are not available until the invocation function handler is called. If you want to define the client instance outside this function, you will need to manually pass in the constructor options ._
|
42 |
|
43 | ```javascript
|
44 | var openwhisk = require('openwhisk');
|
45 | // DOES NOT WORK! Environment parameters not set.
|
46 | var ow = openwhisk();
|
47 |
|
48 | function action() {
|
49 | return ow.actions.invoke('sample')
|
50 | }
|
51 |
|
52 | exports.main = action
|
53 | ```
|
54 |
|
55 | ### outside openwhisk platform
|
56 |
|
57 | ```javascript
|
58 | var openwhisk = require('openwhisk');
|
59 | var options = {apihost: 'openwhisk.ng.bluemix.net', api_key: '...'};
|
60 | var ow = openwhisk(options);
|
61 | ow.actions.invoke('sample').then(result => console.log(result))
|
62 | ```
|
63 |
|
64 | ### constructor options
|
65 |
|
66 | _Client constructor supports the following mandatory parameters:_
|
67 |
|
68 | - **apihost.** Hostname and optional port for openwhisk platform, e.g. `openwhisk.ng.bluemix.net` or `my_whisk_host:80`. Used with API URL template `${protocol}://${apihost}/api/v1/`. If port is missing or port value is 443 in the apihost string, protocol is HTTPS. Otherwise, protocol is HTTP.
|
69 | - **api_key.** Authorisation key for user account registered with OpenWhisk platform.
|
70 |
|
71 | *Client constructor supports the following optional parameters:*
|
72 |
|
73 | - **api.** Full API URL for OpenWhisk platform, e.g. `https://openwhisk.ng.bluemix.net/api/v1/`. This value overrides `apihost` if both are present.
|
74 | - **namespace**. Namespace for resource requests, defaults to `_`.
|
75 | - **ignore_certs**. Turns off server SSL/TLS certificate verification. This allows the client to be used against local deployments of OpenWhisk with a self-signed certificate. Defaults to false.
|
76 | - **apigw_token**. API Gateway service authentication token. This is mandatory for using an external API Gateway service, rather than the built-in api gateway.
|
77 | - **apigw_space_guid**. API Gateway space identifier. This is optional when using an API gateway service, defaults to the authentication uuid.
|
78 |
|
79 | ### environment variables
|
80 |
|
81 | Client constructor will read values for the `apihost`, `namespace`, `api_key`, `apigw_token` and `apigw_space_guid` options from the environment if the following parameters are set. Explicit options have precedence over environment values.
|
82 |
|
83 | - *__OW_API_HOST*
|
84 | - *__OW_NAMESPACE*
|
85 | - *__OW_API_KEY*
|
86 | - *__OW_APIGW_TOKEN*
|
87 | - *__OW_APIGW_SPACE_SUID*
|
88 |
|
89 |
|
90 |
|
91 | ## Examples
|
92 |
|
93 | ### invoke action, blocking for result
|
94 |
|
95 | ```javascript
|
96 | const name = 'reverseWords'
|
97 | const blocking = true, result = true
|
98 | const params = {msg: 'these are some words to reverse'}
|
99 |
|
100 | ow.actions.invoke({name, blocking, result, params}).then(result => {
|
101 | console.log('here is the reversed string', result.reversed)
|
102 | }).catch(err => {
|
103 | console.error('failed to invoke actions', err)
|
104 | })
|
105 | ```
|
106 |
|
107 | ### fire trigger
|
108 |
|
109 | ```javascript
|
110 | const name = 'eventTrigger'
|
111 | const params = {msg: 'event trigger message string'}
|
112 | ow.triggers.invoke({name, params}).then(result => {
|
113 | console.log('trigger fired!')
|
114 | }).catch(err => {
|
115 | console.error('failed to fire trigger', err)
|
116 | })
|
117 | ```
|
118 |
|
119 | ### create action from source file
|
120 |
|
121 | ```javascript
|
122 | const name = 'reverseWords'
|
123 | const action = fs.readFileSync('source.js', {encoding: 'utf8'})
|
124 |
|
125 | ow.actions.create({name, action}).then(result => {
|
126 | console.log('action created!')
|
127 | }).catch(err => {
|
128 | console.error('failed to create action', err)
|
129 | })
|
130 | ```
|
131 |
|
132 | ### create action from zip package
|
133 |
|
134 | ```javascript
|
135 | const name = 'reverseWords'
|
136 | const action = fs.readFileSync('package.zip')
|
137 |
|
138 | ow.actions.create({name, action}).then(result => {
|
139 | console.log('action created!')
|
140 | }).catch(err => {
|
141 | console.error('failed to create action', err)
|
142 | })
|
143 | ```
|
144 |
|
145 | ### retrieve action resource
|
146 |
|
147 | ```javascript
|
148 | const name = 'reverseWords'
|
149 | ow.actions.get(name).then(action => {
|
150 | console.log('action resource', action)
|
151 | }).catch(err => {
|
152 | console.error('failed to retrieve action', err)
|
153 | })
|
154 | ```
|
155 |
|
156 | ### chaining calls
|
157 |
|
158 | ```javascript
|
159 | ow.actions.list()
|
160 | .then(actions => ow.actions.invoke(actions))
|
161 | .then(result => ...)
|
162 | ```
|
163 |
|
164 | ### list packages
|
165 |
|
166 | ```javascript
|
167 | ow.packages.list().then(packages => {
|
168 | packages.forEach(package => console.log(package.name))
|
169 | }).catch(err => {
|
170 | console.error('failed to list packages', err)
|
171 | })
|
172 | ```
|
173 |
|
174 | ### update package parameters
|
175 |
|
176 | ```javascript
|
177 | const name = 'myPackage'
|
178 | const package = {
|
179 | parameters: [
|
180 | {key: "colour", value: "green"},
|
181 | {key: "name", value: "Freya"}
|
182 | ]
|
183 | }
|
184 |
|
185 | ow.packages.update({name, package}).then(package => {
|
186 | console.log('updated package:', package.name)
|
187 | }).catch(err => {
|
188 | console.error('failed to update package', err)
|
189 | })
|
190 | ```
|
191 |
|
192 | ### create trigger feed from alarm package
|
193 |
|
194 | ```javascript
|
195 | // alarmTrigger MUST already exist in default namespace
|
196 | const params = {cron: '*/8 * * * * *', trigger_payload: {name: 'James'}}
|
197 | const name = '/whisk.system/alarms/alarm'
|
198 | const trigger = 'alarmTrigger'
|
199 | ow.feeds.create({name, trigger, params}).then(package => {
|
200 | console.log('alarm trigger feed created')
|
201 | }).catch(err => {
|
202 | console.error('failed to create alarm trigger', err)
|
203 | })
|
204 | ```
|
205 |
|
206 |
|
207 |
|
208 | ## API Details
|
209 |
|
210 | ### resource identifiers + namespaces
|
211 |
|
212 | When passing resource identifiers as parameters you can either use a short name, without an explicit namespace, or a fully-qualified identifier, including namespace and package details.
|
213 |
|
214 | If the namespace is missing from the resource identifier, the client will use the namespace from configuration options following this ordering.
|
215 |
|
216 | - `namespace` from method parameter options OR
|
217 | - `namespace` from options passed into client constructor OR
|
218 | - `namespace` from environment variable (`__OW_NAMESPACE`) OR
|
219 | - default namespace: `_`
|
220 |
|
221 | ### list resources
|
222 |
|
223 | ```javascript
|
224 | ow.actions.list()
|
225 | ow.activations.list()
|
226 | ow.triggers.list()
|
227 | ow.rules.list()
|
228 | ow.namespaces.list()
|
229 | ow.packages.list()
|
230 | ```
|
231 |
|
232 | Query parameters for the API calls are supported (e.g. limit, skip, etc.) by passing an object with the named parameters as the first argument.
|
233 |
|
234 | ```javascript
|
235 | ow.actions.list({skip: 100, limit: 50})
|
236 | ```
|
237 |
|
238 | The following optional parameters are supported:
|
239 | - `namespace` - set custom namespace for endpoint
|
240 |
|
241 | ### retrieve resource
|
242 |
|
243 | ```javascript
|
244 | ow.actions.get({name: '...'})
|
245 | ow.activations.get({name: '...'})
|
246 | ow.triggers.get({name: '...'})
|
247 | ow.rules.get({name: '...'})
|
248 | ow.namespaces.get({name: '...'})
|
249 | ow.packages.get({name: '...'})
|
250 | ow.feeds.get({name: '...'})
|
251 | ```
|
252 |
|
253 | The following optional parameters are supported:
|
254 | - `namespace` - set custom namespace for endpoint
|
255 |
|
256 | This method also supports passing the `name` property directly without wrapping within an object.
|
257 |
|
258 | ```javascript
|
259 | const name = "actionName"
|
260 | ow.actions.get(name)
|
261 | ```
|
262 |
|
263 | If you pass in an array for the first parameter, the `get` call will be executed for each array item. The function returns a Promise which resolves with the results when all operations have finished.
|
264 |
|
265 | ```javascript
|
266 | ow.actions.get(["a", {name: "b"}])
|
267 | ```
|
268 |
|
269 | ### delete resource
|
270 |
|
271 | ```javascript
|
272 | ow.actions.delete({name: '...'})
|
273 | ow.triggers.delete({name: '...'})
|
274 | ow.rules.delete({name: '...'})
|
275 | ow.packages.delete({name: '...'})
|
276 | ow.feeds.delete({name: '...', trigger: '...'})
|
277 | ```
|
278 |
|
279 | The following optional parameters are supported:
|
280 | - `namespace` - set custom namespace for endpoint
|
281 |
|
282 | This method also supports passing the `name` property directly without wrapping within an object.
|
283 |
|
284 | ```javascript
|
285 | const name = "actionName"
|
286 | ow.actions.delete(name)
|
287 | ```
|
288 |
|
289 | If you pass in an array for the first parameter, the `delete` call will be executed for each array item. The function returns a Promise which resolves with the results when all operations have finished.
|
290 |
|
291 | ```javascript
|
292 | ow.actions.delete(["a", {name: "b"}])
|
293 | ```
|
294 |
|
295 | ### invoke action
|
296 |
|
297 | ```javascript
|
298 | ow.actions.invoke({name: '...'})
|
299 | ```
|
300 |
|
301 | The `actionName` parameter supports the following formats: `actionName`, `package/actionName`, `/namespace/actionName`, `/namespace/package/actionName`.
|
302 |
|
303 | If `actionName` includes a namespace, this overrides any other `namespace` properties.
|
304 |
|
305 | The following optional parameters are supported:
|
306 | - `blocking` - delay returning until action has finished executing (default: `false`)
|
307 | - `result` - return function result (`obj.response.result`) rather than entire API result (default: `false`)
|
308 | - `params` - JSON object containing parameters for the action being invoked (default: `{}`)
|
309 | - `namespace` - set custom namespace for endpoint
|
310 |
|
311 | This method also supports passing the `name` property directly without wrapping within an object.
|
312 |
|
313 | ```javascript
|
314 | const name = "actionName"
|
315 | ow.actions.invoke(name)
|
316 | ```
|
317 |
|
318 | If you pass in an array for the first parameter, the `invoke` call will be executed for each array item. The function returns a Promise which resolves with the results when all operations have finished.
|
319 |
|
320 | ```javascript
|
321 | ow.actions.invoke(["a", {name: "b", blocking: true}])
|
322 | ```
|
323 |
|
324 | ### create & update action
|
325 |
|
326 | ```javascript
|
327 | ow.actions.create({name: '...', action: 'function main() {};'})
|
328 | ow.actions.update({name: '...', action: 'function main() {};'})
|
329 | ```
|
330 |
|
331 | The following mandatory parameters are supported:
|
332 | - `name` - action identifier
|
333 | - `action` - String containing JS function source code, Buffer [containing package action zip file](https://github.com/openwhisk/openwhisk/blob/master/docs/actions.md#packaging-an-action-as-a-nodejs-module) or JSON object containing full parameters for the action body
|
334 |
|
335 | The following optional parameters are supported:
|
336 | - `namespace` - set custom namespace for endpoint
|
337 | - `params` - object containing default parameters for the action (default: `{}`)
|
338 | - `annotations` - object containing annotations for the action (default: `{}`)
|
339 | - `limits` - object containing limits for the action (default: `{}`)
|
340 | - `kind` - runtime environment parameter, ignored when `action` is an object (default: `nodejs:default`)
|
341 | - `version` - set semantic version of the action. If parameter is empty when create new action openwisk generate 0.0.1 value when update an action increase the patch version.
|
342 |
|
343 | If you pass in an array for the first parameter, the `create` call will be executed for each array item. The function returns a Promise which resolves with the results when all operations have finished.
|
344 |
|
345 | ```javascript
|
346 | ow.actions.create([{...}, {...}])
|
347 | ```
|
348 |
|
349 | ### fire trigger
|
350 |
|
351 | ```javascript
|
352 | ow.triggers.invoke({name: '...'})
|
353 | ```
|
354 |
|
355 | The following optional parameters are supported:
|
356 | - `params` - JSON object containing parameters for the trigger being fired (default: `{}`)
|
357 | - `namespace` - set custom namespace for endpoint
|
358 |
|
359 | This method also supports passing the `name` property directly without wrapping within an object.
|
360 |
|
361 | ```javascript
|
362 | const name = "actionName"
|
363 | ow.triggers.invoke(name)
|
364 | ```
|
365 |
|
366 | If you pass in an array for the first parameter, the `invoke` call will be executed for each array item. The function returns a Promise which resolves with the results when all operations have finished.
|
367 |
|
368 | ```javascript
|
369 | ow.triggers.invoke(["a", {name: "b", blocking: true}])
|
370 | ```
|
371 |
|
372 | ### create & update trigger
|
373 |
|
374 | ```javascript
|
375 | ow.triggers.create({name: '...'})
|
376 | ow.triggers.update({name: '...'})
|
377 | ```
|
378 |
|
379 | The following optional parameters are supported:
|
380 | - `trigger` - JSON object containing parameters for the trigger body (default: `{}`)
|
381 | - `namespace` - set custom namespace for endpoint
|
382 |
|
383 | ### create & update packages
|
384 |
|
385 | ```javascript
|
386 | ow.packages.create({name: '...'})
|
387 | ow.packages.update({name: '...'})
|
388 | ```
|
389 |
|
390 | The following optional parameters are supported:
|
391 | - `package` - JSON object containing parameters for the package body (default: `{}`)
|
392 | - `namespace` - set custom namespace for endpoint
|
393 |
|
394 | ### create & update rule
|
395 |
|
396 | ```javascript
|
397 | ow.rules.create({name: '...', action: '...', trigger: '...'})
|
398 | ow.rules.update({name: '...', action: '...', trigger: '...'})
|
399 | ```
|
400 |
|
401 | `trigger` and `action` identifiers will have the default namespace (`/_/`)
|
402 | appended in the request, unless a fully qualified name is passed in
|
403 | (`/custom_ns/action_or_trigger_name`).
|
404 |
|
405 | The following optional parameters are supported:
|
406 | - `namespace` - set namespace for rule
|
407 |
|
408 | ### enable & disable rule
|
409 |
|
410 | ```javascript
|
411 | ow.rules.enable({name: '...'})
|
412 | ow.rules.disable({name: '...'})
|
413 | ```
|
414 |
|
415 | The following optional parameters are supported:
|
416 | - `namespace` - set custom namespace for endpoint
|
417 |
|
418 | ### create feeds
|
419 |
|
420 | ```javascript
|
421 | ow.feeds.create({feedName: '...', trigger: '...'})
|
422 | ```
|
423 |
|
424 | The following optional parameters are supported:
|
425 | - `namespace` - set custom namespace for endpoint
|
426 | - `params` - JSON object containing parameters for the feed being invoked (default: `{}`)
|
427 |
|
428 | ## api gateway
|
429 |
|
430 | OpenWhisk supports a [built-in API gateway service](https://github.com/apache/incubator-openwhisk/blob/master/docs/apigateway.md) and external third-party providers.
|
431 |
|
432 | This client library defaults to using the platform service. If the `apigw_token` parameter is passed into the client constructor, the implementation will switch to the [IBM Bluemix API Gateway](https://console.ng.bluemix.net/docs/openwhisk/openwhisk_apigateway.html#openwhisk_apigateway).
|
433 |
|
434 | *The interface for managing routes through the library does not change between providers.*
|
435 |
|
436 | ### retrieve route
|
437 |
|
438 | ```javascript
|
439 | ow.routes.get({basepath: '...'})
|
440 | ow.routes.get({name: '...'})
|
441 | ```
|
442 |
|
443 | *This method is a wrapper for the list method. It throws an error if the base path or name parameter is missing.*
|
444 |
|
445 | ### list routes
|
446 |
|
447 | ```javascript
|
448 | ow.routes.list()
|
449 | ```
|
450 |
|
451 | The following optional parameters are supported to filter the result set:
|
452 | - `relpath` - relative URI path for endpoints
|
453 | - `basepath` - base URI path for endpoints
|
454 | - `name` - identifier for API
|
455 | - `operation` - HTTP methods
|
456 | - `limit` - limit result set size
|
457 | - `skip` - skip results from index
|
458 |
|
459 | *`relpath` is only valid when `basepath` is also specified. `name` and `basepath` cannot be used together.*
|
460 |
|
461 | ### delete routes
|
462 |
|
463 | ```javascript
|
464 | ow.routes.delete({basepath: '...'})
|
465 | ow.routes.delete({name: '...'})
|
466 | ```
|
467 |
|
468 | The following optional parameters are supported to filter the result set:
|
469 | - `relpath` - relative URI path for endpoints
|
470 | - `operation` - HTTP methods
|
471 |
|
472 | ### add route
|
473 | ```javascript
|
474 | ow.routes.create({relpath: '...', operation: '...', action: '...'})
|
475 | ```
|
476 |
|
477 | *`action` supports normal (actionName) and fully-qualified (/namespace/actionName) formats.*
|
478 |
|
479 | The following optional parameters are supported:
|
480 | - `responsetype` - content type returned by web action, possible values: `html`, `http`, `json`, `text` and `svg` (default: `json`).
|
481 | - `basepath` - base URI path for endpoints (default: `/`)
|
482 | - `name` - identifier for API (default: `basepath`)
|
483 |
|
484 | ### add route (swagger)
|
485 |
|
486 | ```javascript
|
487 | ow.routes.create({swagger: '{...}'})
|
488 | ```
|
489 |
|
490 | Swagger parameter must be a well-formed JSON string, containing a valid Swagger API definition, which follows the [OpenWhisk API Gateway route schema](https://github.com/apache/incubator-openwhisk-apigateway/blob/master/doc/v2/management_interface_v2.md#post-v2tenant_idapis).
|
491 |
|
492 | *No other parameters are supported when creating the route from a JSON Swagger document.*
|
493 |
|
494 | ## Debugging
|
495 |
|
496 | Setting an environment parameter (`NODE_DEBUG=request`) will dump the HTTP requests from the client library and responses received to `stderr`.
|
497 |
|
498 | ```bash
|
499 | NODE_DEBUG=request node script.js
|
500 | ```
|
501 |
|
502 | This parameter can also be set dynamically at runtime, provided this happens before the `openwhisk` module is required.
|
503 |
|
504 | ```javascript
|
505 | process.env.NODE_DEBUG='request';
|
506 | var openwhisk = require('openwhisk');
|
507 | ```
|
508 |
|
509 | ## Development
|
510 |
|
511 | ### unit tests
|
512 |
|
513 | ```bash
|
514 | $ npm test
|
515 | ```
|
516 |
|
517 | ### integration tests
|
518 |
|
519 | *Please [see the instructions](https://github.com/openwhisk/openwhisk-client-js/tree/master/test/integration) for setting up the integration test environment prior to running these tests.*
|
520 |
|
521 | ```bash
|
522 | $ npm run-script test-integration
|
523 | ```
|
524 |
|
525 | **Note:** The test integration runs in secure mode by default, which means that all trusted signers must be present and available to the client process.
|
526 | If your local environment is using self-signed certificates, you can use the following command to start the script in insecure mode:
|
527 |
|
528 | `npm run test-integration -i`
|
529 |
|
530 | This will disable SSL/TLS verification for all SSL communication.
|
531 |
|
532 | Alternatively, you can run the `prepIntegrationTests.sh` script using guest credentials or by specifying specific credentials.
|
533 | Run the script with openwhisk credentials:
|
534 | ```bash
|
535 | $ ./test/integration/prepIntegrationTests.sh <your key in the form of ABCD:EFGH> <openwhisk instance hostname> <openwhisk namespace> <api gatewaytoken>
|
536 | ```
|
537 | The `prepIntegrationTests.sh` script is designed to give you feedback if it detects a setting that is not correct on your machine. ex: `node 6 or above is not detected`
|
538 |
|
539 | ## Code-Coverage:
|
540 |
|
541 | You can customize how comprehensive the tests are over the code, and generate reports to view the results by using
|
542 | the provided `code-coverage` commands below.
|
543 |
|
544 | **Note:** Ensure that you use guest credentials with the wsk CLI.
|
545 |
|
546 | To compile down to ECMA5 run the following command:
|
547 | 1 `$ npm run code-coverage-build`
|
548 |
|
549 | To generate combined reports of both the unit and integration tests, run the following command:
|
550 | 2 `$ npm run code-coverage-run <key> <host> <namespace> <token> <options>`
|
551 |
|
552 | The report is viewable under `/coverage`. Click **`/coverage/index.html`** to view the full report.
|