Add support for new API Gateway service. (#50)
* Adding support for third-party API gateway provides.
Passing token will switch to using provider implementation
rather than built-in platform service.
* 3.6.0
* Fixing broken links and README.
diff --git a/README.md b/README.md
index bcbc188..27261c0 100644
--- a/README.md
+++ b/README.md
@@ -68,16 +68,19 @@
- **api.** Full API URL for OpenWhisk platform, e.g. `https://openwhisk.ng.bluemix.net/api/v1/`. This value overrides `apihost` if both are present.
- **namespace**. Namespace for resource requests, defaults to `_`.
-
- **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.
+- **apigw_token**. API Gateway service authentication token. This is mandatory for using an external API Gateway service, rather than the built-in api gateway.
+- **apigw_space_guid**. API Gateway space identifier. This is optional when using an API gateway service, defaults to the authentication uuid.
### environment variables
-Client constructor will read values for the `apihost`, `namespace` and `api_key` options from the environment if the following parameters are set. Explicit parameter values override these values.
+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.
-- *\_\_OW_API_HOST*
-- *\_\_OW_NAMESPACE*
+- *__OW_API_HOST*
+- *__OW_NAMESPACE*
- *__OW_API_KEY*
+- *__OW_APIGW_TOKEN*
+- *__OW_APIGW_SPACE_SUID*
@@ -414,9 +417,13 @@
- `namespace` - set custom namespace for endpoint
- `params` - JSON object containing parameters for the feed being invoked (default: `{}`)
-## api gateway (experimental)
+## api gateway
-*[API Gateway support](https://github.com/openwhisk/openwhisk/blob/master/docs/apigateway.md) is currently experimental and may be subject to breaking changes.*
+OpenWhisk supports a [built-in API gateway service](https://github.com/apache/incubator-openwhisk/blob/master/docs/apigateway.md) and external third-party providers.
+
+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).
+
+*The interface for managing routes through the library does not change between providers.*
### list routes
diff --git a/lib/client.js b/lib/client.js
index f304913..38b7453 100644
--- a/lib/client.js
+++ b/lib/client.js
@@ -18,13 +18,22 @@
const api = options.api ||
this.url_from_apihost(options.apihost || process.env['__OW_API_HOST'])
+ // optional tokens for API GW service
+ const apigw_token = options.apigw_token || process.env['__OW_APIGW_TOKEN']
+ let apigw_space_guid = options.apigw_space_guid || process.env['__OW_APIGW_SPACE_GUID']
+
+ // unless space is explicitly passed, default to using auth uuid.
+ if (apigw_token && !apigw_space_guid) {
+ apigw_space_guid = api_key.split(':')[0]
+ }
+
if (!api_key) {
throw new Error(`${messages.INVALID_OPTIONS_ERROR} Missing api_key parameter.`)
} else if (!api) {
throw new Error(`${messages.INVALID_OPTIONS_ERROR} Missing either api or apihost parameters.`)
}
- return {api_key, api, ignore_certs, namespace: options.namespace}
+ return {api_key, api, ignore_certs, namespace: options.namespace, apigw_token, apigw_space_guid}
}
url_from_apihost (apihost) {
diff --git a/lib/routes.js b/lib/routes.js
index b716064..a7fc1b5 100644
--- a/lib/routes.js
+++ b/lib/routes.js
@@ -6,14 +6,27 @@
const CREATE_PARAMS = ['relpath', 'operation', 'action']
class Routes extends BaseOperation {
- static get routeMgmtApiPath () {
- return 'experimental/web/whisk.system/routemgmt'
+ routeMgmtApiPath (path) {
+ return this.has_access_token() ?
+ `web/whisk.system/apimgmt/${path}.http` :
+ `experimental/web/whisk.system/routemgmt/${path}.json`
}
list (options) {
options = options || {}
const qs = this.qs(options, ['relpath', 'basepath', 'operation', 'limit', 'skip'])
- return this.client.request('GET', `${Routes.routeMgmtApiPath}/getApi.json`, { qs })
+ return this.client.request('GET', this.routeMgmtApiPath('getApi'), { qs })
+ }
+
+ qs (options, names) {
+ const result = super.qs(options, names)
+
+ if (this.has_access_token()) {
+ result.accesstoken = this.client.options.apigw_token
+ result.spaceguid = this.client.options.apigw_space_guid
+ }
+
+ return result
}
delete (options) {
@@ -23,7 +36,7 @@
const qs = this.qs(options, ['relpath', 'basepath', 'operation'])
qs.force = true
- return this.client.request('DELETE', `${Routes.routeMgmtApiPath}/deleteApi.json`, { qs })
+ return this.client.request('DELETE', this.routeMgmtApiPath('deleteApi'), { qs })
}
create (options) {
@@ -34,7 +47,8 @@
}
const body = this.route_swagger_definition(options)
- return this.client.request('POST', `${Routes.routeMgmtApiPath}/createApi.json`, { body })
+ const qs = this.qs(options, [])
+ return this.client.request('POST', this.routeMgmtApiPath('createApi'), { body, qs })
}
route_swagger_definition (params) {
@@ -59,7 +73,7 @@
return {
name: id,
namespace: namespace,
- backendMethod: 'POST',
+ backendMethod: this.action_url_method(),
backendUrl: this.action_url_path(id, namespace),
authkey: this.client.options.api_key
}
@@ -69,9 +83,26 @@
return params.basepath || '/'
}
+ action_url_method () {
+ return this.has_access_token() ? 'GET' : 'POST'
+ }
+
action_url_path (id, namespace) {
- const path = `namespaces/${namespace}/actions/${id}`
- return this.client.path_url(path)
+ if (!this.has_access_token()) {
+ return this.client.path_url(`namespaces/${namespace}/actions/${id}`)
+ }
+
+ // web action path must contain package identifier. uses default for
+ // non-explicit package.
+ if (!id.includes('/')) {
+ id = `default/${id}`
+ }
+
+ return this.client.path_url(`web/${namespace}/${id}.http`)
+ }
+
+ has_access_token () {
+ return !!this.client.options.apigw_token
}
}
diff --git a/package.json b/package.json
index 92b2509..efbb4c5 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "openwhisk",
- "version": "3.5.1",
+ "version": "3.6.0",
"description": "JavaScript client library for the OpenWhisk platform",
"main": "lib/main.js",
"engines": {
diff --git a/test/integration/README.md b/test/integration/README.md
index 297b8fc..54fec55 100644
--- a/test/integration/README.md
+++ b/test/integration/README.md
@@ -3,13 +3,12 @@
Running the integration tests requires the following environment variables to be defined.
- export OW_API_KEY=<your api key>
- export OW_API_URL=<openwhisk API url>
- export __OW_NAMESPACE=<namespace to test against>
+ export __OW_API_KEY=<your api key>
+ export __OW_API_HOST=<openwhisk API hostname>
+ export __OW_NAMESPACE=<openwhisk namespace>
+ export __OW_APIGW_TOKEN=<api gateway token>
-You can retrieve these settings using the `wsk` CLI:
-
- wsk property get --all
+You can retrieve these settings from the `.wskprops` file.
Further, you need to create the following seed artifacts.
diff --git a/test/integration/actions.test.js b/test/integration/actions.test.js
index c335cec..bf5e204 100644
--- a/test/integration/actions.test.js
+++ b/test/integration/actions.test.js
@@ -5,26 +5,20 @@
const Client = require('../../lib/client.js')
const JSZip = require('jszip')
-const API_KEY = process.env.OW_API_KEY
-const API_URL = process.env.OW_API_URL
-const NAMESPACE = process.env['__OW_NAMESPACE']
+const envParams = ['API_KEY', 'API_HOST', 'NAMESPACE']
-if (!API_KEY) {
- throw new Error('Missing OW_API_KEY environment parameter')
-}
+// check that mandatory configuration properties are available
+envParams.forEach(key => {
+ const param = `__OW_${key}`
+ if (!process.env.hasOwnProperty(param)) {
+ throw new Error(`Missing ${param} environment parameter`)
+ }
+})
-if (!API_URL) {
- throw new Error('Missing OW_API_URL environment parameter')
-}
-
-if (!NAMESPACE) {
- throw new Error('Missing __OW_NAMESPACE environment parameter')
-}
+const NAMESPACE = process.env.__OW_NAMESPACE
test('list all actions using default namespace', t => {
- const params = {api: API_URL, api_key: API_KEY}
-
- const actions = new Actions(new Client(params))
+ const actions = new Actions(new Client())
return actions.list().then(result => {
t.true(Array.isArray(result))
result.forEach(action => {
@@ -38,9 +32,7 @@
})
test('list all actions using options namespace', t => {
- const params = {api: API_URL, api_key: API_KEY}
-
- const actions = new Actions(new Client(params))
+ const actions = new Actions(new Client())
return actions.list({namespace: NAMESPACE}).then(result => {
t.true(Array.isArray(result))
result.forEach(action => {
@@ -54,14 +46,12 @@
})
test('create, get and delete an action', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
const errors = err => {
console.log(err)
t.fail()
}
- const actions = new Actions(new Client(params))
+ const actions = new Actions(new Client())
return actions.create({actionName: 'random_action_test', action: 'function main() {return {payload:"testing"}}'}).then(result => {
t.is(result.name, 'random_action_test')
t.is(result.namespace, NAMESPACE)
@@ -77,14 +67,12 @@
})
test('create and update an action', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
const errors = err => {
console.log(err)
t.fail()
}
- const actions = new Actions(new Client(params))
+ const actions = new Actions(new Client())
return actions.create({actionName: 'random_update_tested', action: 'function main() {return {payload:"testing"}}'}).then(result => {
t.is(result.name, 'random_update_tested')
t.is(result.namespace, NAMESPACE)
@@ -99,14 +87,12 @@
})
test('create, get and delete with parameters an action', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
const errors = err => {
console.log(err)
t.fail()
}
- const actions = new Actions(new Client(params))
+ const actions = new Actions(new Client())
return actions.create({name: 'random_action_params_test', params: { hello: 'world' }, action: 'function main() {return {payload:"testing"}}'}).then(result => {
t.is(result.name, 'random_action_params_test')
t.is(result.namespace, NAMESPACE)
@@ -124,15 +110,13 @@
})
test('invoke action with fully-qualified name', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
const errors = err => {
console.log(err)
console.dir(err)
t.fail()
}
- const actions = new Actions(new Client(params))
+ const actions = new Actions(new Client())
return actions.invoke({actionName: '/whisk.system/utils/sort', blocking: true}).then(invoke_result => {
t.true(invoke_result.response.success)
t.pass()
@@ -140,8 +124,6 @@
})
test('create, invoke and remove package action', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
const errors = err => {
console.log(err)
console.dir(err)
@@ -153,7 +135,7 @@
zip.file('index.js', 'function main(params) {return params};\nexports.main = main;')
return zip.generateAsync({type: 'nodebuffer'}).then(content => {
- const actions = new Actions(new Client(params))
+ const actions = new Actions(new Client())
return actions.create({actionName: 'random_package_action_test', action: content}).then(result => {
return actions.invoke({actionName: 'random_package_action_test', params: {hello: 'world'}, blocking: true}).then(invoke_result => {
t.deepEqual(invoke_result.response.result, {hello: 'world'})
diff --git a/test/integration/activations.test.js b/test/integration/activations.test.js
index ea75526..fed912a 100644
--- a/test/integration/activations.test.js
+++ b/test/integration/activations.test.js
@@ -4,26 +4,20 @@
const Activations = require('../../lib/activations.js')
const Client = require('../../lib/client.js')
-const API_KEY = process.env.OW_API_KEY
-const API_URL = process.env.OW_API_URL
-const NAMESPACE = process.env['__OW_NAMESPACE']
+const envParams = ['API_KEY', 'API_HOST', 'NAMESPACE']
-if (!API_KEY) {
- throw new Error('Missing OW_API_KEY environment parameter')
-}
+// check that mandatory configuration properties are available
+envParams.forEach(key => {
+ const param = `__OW_${key}`
+ if (!process.env.hasOwnProperty(param)) {
+ throw new Error(`Missing ${param} environment parameter`)
+ }
+})
-if (!API_URL) {
- throw new Error('Missing OW_API_URL environment parameter')
-}
-
-if (!NAMESPACE) {
- throw new Error('Missing __OW_NAMESPACE environment parameter')
-}
+const NAMESPACE = process.env.__OW_NAMESPACE
test('list all activations', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
- const activations = new Activations(new Client(params))
+ const activations = new Activations(new Client())
return activations.list().then(result => {
t.true(Array.isArray(result))
result.forEach(r => t.is(r.namespace, NAMESPACE))
@@ -35,9 +29,7 @@
})
test('retrieve individual activations', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
- const activations = new Activations(new Client(params))
+ const activations = new Activations(new Client())
return activations.list().then(result => {
t.true(Array.isArray(result))
return Promise.all(result.map(r => activations.get({activation: r.activationId})))
@@ -48,9 +40,7 @@
})
test('retrieve individual activation logs', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
- const activations = new Activations(new Client(params))
+ const activations = new Activations(new Client())
return activations.list().then(result => {
t.true(Array.isArray(result))
return Promise.all(result.map(r => activations.logs({activation: r.activationId})))
@@ -61,9 +51,7 @@
})
test('retrieve individual activation result', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
- const activations = new Activations(new Client(params))
+ const activations = new Activations(new Client())
return activations.list().then(result => {
t.true(Array.isArray(result))
return Promise.all(result.map(r => activations.result({activation: r.activationId})))
diff --git a/test/integration/feeds.test.js b/test/integration/feeds.test.js
index 238b640..6f1bd44 100644
--- a/test/integration/feeds.test.js
+++ b/test/integration/feeds.test.js
@@ -5,32 +5,26 @@
const Triggers = require('../../lib/triggers.js')
const Client = require('../../lib/client.js')
-const API_KEY = process.env.OW_API_KEY
-const API_URL = process.env.OW_API_URL
-const NAMESPACE = process.env['__OW_NAMESPACE']
+const envParams = ['API_KEY', 'API_HOST', 'NAMESPACE']
-if (!API_KEY) {
- throw new Error('Missing OW_API_KEY environment parameter')
-}
+// check that mandatory configuration properties are available
+envParams.forEach(key => {
+ const param = `__OW_${key}`
+ if (!process.env.hasOwnProperty(param)) {
+ throw new Error(`Missing ${param} environment parameter`)
+ }
+})
-if (!API_URL) {
- throw new Error('Missing OW_API_URL environment parameter')
-}
-
-if (!NAMESPACE) {
- throw new Error('Missing OW_NAMESPACE environment parameter')
-}
+const NAMESPACE = process.env.__OW_NAMESPACE
test('create and delete a feed', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
const errors = err => {
console.log(err)
t.fail()
}
- const feeds = new Feeds(new Client(params))
- const triggers = new Triggers(new Client(params))
+ const feeds = new Feeds(new Client())
+ const triggers = new Triggers(new Client())
const feed_params = {
feedName: '/whisk.system/alarms/alarm',
trigger: `/${NAMESPACE}/sample_feed_trigger`,
diff --git a/test/integration/namespaces.test.js b/test/integration/namespaces.test.js
index 3b0cb58..6bf1825 100644
--- a/test/integration/namespaces.test.js
+++ b/test/integration/namespaces.test.js
@@ -4,26 +4,20 @@
const Namespaces = require('../../lib/namespaces.js')
const Client = require('../../lib/client.js')
-const API_KEY = process.env.OW_API_KEY
-const API_URL = process.env.OW_API_URL
-const NAMESPACE = process.env['__OW_NAMESPACE']
+const envParams = ['API_KEY', 'API_HOST', 'NAMESPACE']
-if (!API_KEY) {
- throw new Error('Missing OW_API_KEY environment parameter')
-}
+// check that mandatory configuration properties are available
+envParams.forEach(key => {
+ const param = `__OW_${key}`
+ if (!process.env.hasOwnProperty(param)) {
+ throw new Error(`Missing ${param} environment parameter`)
+ }
+})
-if (!API_URL) {
- throw new Error('Missing OW_API_URL environment parameter')
-}
-
-if (!NAMESPACE) {
- throw new Error('Missing OW_NAMESPACE environment parameter')
-}
+const NAMESPACE = process.env.__OW_NAMESPACE
test('list all namespaces', t => {
- const params = {api: API_URL, api_key: API_KEY}
-
- const namespaces = new Namespaces(new Client(params))
+ const namespaces = new Namespaces(new Client())
return namespaces.list().then(result => {
t.true(Array.isArray(result))
t.pass()
@@ -34,9 +28,7 @@
})
test('retrieve individual namespaces', t => {
- const params = {api: API_URL, api_key: API_KEY}
-
- const namespaces = new Namespaces(new Client(params))
+ const namespaces = new Namespaces(new Client())
return namespaces.list().then(result => {
t.true(Array.isArray(result))
return Promise.all(result.map(ns => namespaces.get({namespace: ns})))
diff --git a/test/integration/packages.test.js b/test/integration/packages.test.js
index a8d96d5..059d5dd 100644
--- a/test/integration/packages.test.js
+++ b/test/integration/packages.test.js
@@ -4,26 +4,20 @@
const Packages = require('../../lib/packages.js')
const Client = require('../../lib/client.js')
-const API_KEY = process.env.OW_API_KEY
-const API_URL = process.env.OW_API_URL
-const NAMESPACE = process.env['__OW_NAMESPACE']
+const envParams = ['API_KEY', 'API_HOST', 'NAMESPACE']
-if (!API_KEY) {
- throw new Error('Missing OW_API_KEY environment parameter')
-}
+// check that mandatory configuration properties are available
+envParams.forEach(key => {
+ const param = `__OW_${key}`
+ if (!process.env.hasOwnProperty(param)) {
+ throw new Error(`Missing ${param} environment parameter`)
+ }
+})
-if (!API_URL) {
- throw new Error('Missing OW_API_URL environment parameter')
-}
-
-if (!NAMESPACE) {
- throw new Error('Missing OW_NAMESPACE environment parameter')
-}
+const NAMESPACE = process.env.__OW_NAMESPACE
test('list all packages using default namespace', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
- const packages = new Packages(new Client(params))
+ const packages = new Packages(new Client())
return packages.list().then(result => {
t.true(Array.isArray(result))
result.forEach(packageName => {
@@ -37,9 +31,7 @@
})
test('list all packages using options namespace', t => {
- const params = {api: API_URL, api_key: API_KEY}
-
- const packages = new Packages(new Client(params))
+ const packages = new Packages(new Client())
return packages.list({namespace: NAMESPACE}).then(result => {
t.true(Array.isArray(result))
result.forEach(packageName => {
@@ -53,14 +45,12 @@
})
test('create, get and delete an package', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
const errors = err => {
console.log(err)
t.fail()
}
- const packages = new Packages(new Client(params))
+ const packages = new Packages(new Client())
return packages.create({packageName: 'random_package_test'}).then(result => {
t.is(result.name, 'random_package_test')
t.is(result.namespace, NAMESPACE)
@@ -76,14 +66,12 @@
})
test('create and update an package', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
const errors = err => {
console.log(err)
t.fail()
}
- const packages = new Packages(new Client(params))
+ const packages = new Packages(new Client())
return packages.create({packageName: 'random_package_update_test'}).then(result => {
t.is(result.name, 'random_package_update_test')
t.is(result.namespace, NAMESPACE)
diff --git a/test/integration/routes.test.js b/test/integration/routes.test.js
index b855bf4..6ae02fb 100644
--- a/test/integration/routes.test.js
+++ b/test/integration/routes.test.js
@@ -5,27 +5,22 @@
const Routes = require('../../lib/routes.js')
const Client = require('../../lib/client.js')
-const API_KEY = process.env.OW_API_KEY
-const API_URL = process.env.OW_API_URL
-const NAMESPACE = process.env['__OW_NAMESPACE']
+const envParams = ['API_KEY', 'API_HOST', 'APIGW_TOKEN']
-if (!API_KEY) {
- throw new Error('Missing OW_API_KEY environment parameter')
-}
+// check that mandatory configuration properties are available
+envParams.forEach(key => {
+ const param = `__OW_${key}`
+ if (!process.env.hasOwnProperty(param)) {
+ throw new Error(`Missing ${param} environment parameter`)
+ }
+})
-if (!API_URL) {
- throw new Error('Missing OW_API_URL environment parameter')
-}
-
-if (!NAMESPACE) {
- throw new Error('Missing OW_NAMESPACE environment parameter')
-}
-
-test('create, retrieve and delete action route', t => {
- const params = {api: API_URL, api_key: API_KEY}
-
- const routes = new Routes(new Client(params))
- const actions = new Actions(new Client(params))
+test.serial('create, retrieve and delete action route', t => {
+ const token = process.env.__OW_APIGW_TOKEN
+ delete process.env.__OW_APIGW_TOKEN
+ const routes = new Routes(new Client())
+ const actions = new Actions(new Client())
+ process.env.__OW_APIGW_TOKEN = token
return actions.create({actionName: 'routeAction', action: ''}).then(() => {
return routes.create({action: 'routeAction', basepath: '/testing', relpath: '/foo/bar', operation: 'POST'}).then(() => {
@@ -46,3 +41,27 @@
t.fail()
})
})
+
+test.serial('create, retrieve and delete action route using token', t => {
+ const routes = new Routes(new Client())
+ const actions = new Actions(new Client())
+
+ return actions.create({actionName: 'routeAction', action: ''}).then(() => {
+ return routes.create({action: 'routeAction', basepath: '/testing', relpath: '/foo/bar', operation: 'POST'}).then(() => {
+ return routes.list({basepath: '/testing'}).then(results => {
+ t.is(results.apis.length, 1)
+ const apidoc = results.apis[0].value.apidoc
+ t.is(apidoc.basePath, '/testing')
+ t.true(apidoc.paths.hasOwnProperty('/foo/bar'))
+ const path = apidoc.paths['/foo/bar']
+ t.true(path.hasOwnProperty('post'))
+ t.is(path.post['x-openwhisk'].action, 'routeAction')
+
+ return routes.delete({basepath: '/testing'}).then(() => actions.delete({actionName: 'routeAction'}))
+ })
+ })
+ }).catch(err => {
+ console.log(err)
+ t.fail()
+ })
+})
diff --git a/test/integration/rules.test.js b/test/integration/rules.test.js
index ed05e34..22bcdda 100644
--- a/test/integration/rules.test.js
+++ b/test/integration/rules.test.js
@@ -5,26 +5,20 @@
const Triggers = require('../../lib/triggers.js')
const Client = require('../../lib/client.js')
-const API_KEY = process.env.OW_API_KEY
-const API_URL = process.env.OW_API_URL
-const NAMESPACE = process.env['__OW_NAMESPACE']
+const envParams = ['API_KEY', 'API_HOST', 'NAMESPACE']
-if (!API_KEY) {
- throw new Error('Missing OW_API_KEY environment parameter')
-}
+// check that mandatory configuration properties are available
+envParams.forEach(key => {
+ const param = `__OW_${key}`
+ if (!process.env.hasOwnProperty(param)) {
+ throw new Error(`Missing ${param} environment parameter`)
+ }
+})
-if (!API_URL) {
- throw new Error('Missing OW_API_URL environment parameter')
-}
-
-if (!NAMESPACE) {
- throw new Error('Missing OW_NAMESPACE environment parameter')
-}
+const NAMESPACE = process.env.__OW_NAMESPACE
test('list all rules using default namespace', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
- const rules = new Rules(new Client(params))
+ const rules = new Rules(new Client())
return rules.list().then(result => {
t.true(Array.isArray(result))
result.forEach(rule => {
@@ -38,9 +32,7 @@
})
test('list all rules using options namespace', t => {
- const params = {api: API_URL, api_key: API_KEY}
-
- const rules = new Rules(new Client(params))
+ const rules = new Rules(new Client())
return rules.list({namespace: NAMESPACE}).then(result => {
t.true(Array.isArray(result))
result.forEach(rule => {
@@ -55,15 +47,13 @@
// Running update tests conconcurrently leads to resource conflict errors.
test.serial('create, get and delete a rule', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
const errors = err => {
console.log(err)
t.fail()
}
- const rules = new Rules(new Client(params))
- const triggers = new Triggers(new Client(params))
+ const rules = new Rules(new Client())
+ const triggers = new Triggers(new Client())
return triggers.create({triggerName: 'sample_rule_trigger'}).then(() => {
return rules.create({ruleName: 'random_rule_test', action: `/${NAMESPACE}/hello`, trigger: `/${NAMESPACE}/sample_rule_trigger`}).then(result => {
t.is(result.name, 'random_rule_test')
@@ -83,15 +73,13 @@
})
test.serial('create and update a rule', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
const errors = err => {
console.log(err)
t.fail()
}
- const rules = new Rules(new Client(params))
- const triggers = new Triggers(new Client(params))
+ const rules = new Rules(new Client())
+ const triggers = new Triggers(new Client())
return triggers.create({triggerName: 'sample_rule_trigger'}).then(() => {
return rules.create({ruleName: 'random_update_test', action: `/${NAMESPACE}/hello`, trigger: `/${NAMESPACE}/sample_rule_trigger`}).then(result => {
t.is(result.name, 'random_update_test')
diff --git a/test/integration/triggers.test.js b/test/integration/triggers.test.js
index 18543f5..b976411 100644
--- a/test/integration/triggers.test.js
+++ b/test/integration/triggers.test.js
@@ -4,26 +4,20 @@
const Triggers = require('../../lib/triggers.js')
const Client = require('../../lib/client.js')
-const API_KEY = process.env.OW_API_KEY
-const API_URL = process.env.OW_API_URL
-const NAMESPACE = process.env['__OW_NAMESPACE']
+const envParams = ['API_KEY', 'API_HOST', 'NAMESPACE']
-if (!API_KEY) {
- throw new Error('Missing OW_API_KEY environment parameter')
-}
+// check that mandatory configuration properties are available
+envParams.forEach(key => {
+ const param = `__OW_${key}`
+ if (!process.env.hasOwnProperty(param)) {
+ throw new Error(`Missing ${param} environment parameter`)
+ }
+})
-if (!API_URL) {
- throw new Error('Missing OW_API_URL environment parameter')
-}
-
-if (!NAMESPACE) {
- throw new Error('Missing OW_NAMESPACE environment parameter')
-}
+const NAMESPACE = process.env.__OW_NAMESPACE
test('list all triggers using default namespace', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
- const triggers = new Triggers(new Client(params))
+ const triggers = new Triggers(new Client())
return triggers.list().then(result => {
t.true(Array.isArray(result))
result.forEach(trigger => {
@@ -37,9 +31,7 @@
})
test('list all triggers using options namespace', t => {
- const params = {api: API_URL, api_key: API_KEY}
-
- const triggers = new Triggers(new Client(params))
+ const triggers = new Triggers(new Client())
return triggers.list({namespace: NAMESPACE}).then(result => {
t.true(Array.isArray(result))
result.forEach(trigger => {
@@ -53,14 +45,12 @@
})
test('create, get and delete an trigger', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
const errors = err => {
console.log(err)
t.fail()
}
- const triggers = new Triggers(new Client(params))
+ const triggers = new Triggers(new Client())
return triggers.create({triggerName: 'random_trigger_test'}).then(result => {
t.is(result.name, 'random_trigger_test')
t.is(result.namespace, NAMESPACE)
@@ -78,14 +68,12 @@
})
test('create and update an trigger', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
const errors = err => {
console.log(err)
t.fail()
}
- const triggers = new Triggers(new Client(params))
+ const triggers = new Triggers(new Client())
return triggers.create({triggerName: 'random_create_update_test'}).then(result => {
t.is(result.name, 'random_create_update_test')
t.is(result.namespace, NAMESPACE)
@@ -101,14 +89,12 @@
})
test('fire a trigger', t => {
- const params = {api: API_URL, api_key: API_KEY, namespace: NAMESPACE}
-
const errors = err => {
console.log(err)
t.fail()
}
- const triggers = new Triggers(new Client(params))
+ const triggers = new Triggers(new Client())
return triggers.create({triggerName: 'random_fire_test'}).then(result => {
return triggers.invoke({triggerName: 'random_fire_test'}).then(update_result => {
t.true(update_result.hasOwnProperty('activationId'))
diff --git a/test/unit/client.test.js b/test/unit/client.test.js
index 5526ed6..c7eaed3 100644
--- a/test/unit/client.test.js
+++ b/test/unit/client.test.js
@@ -13,30 +13,49 @@
})
test('should support explicit constructor options', t => {
- const client = new Client({namespace: 'ns', ignore_certs: true, api_key: 'aaa', api: 'my_host'})
+ const client = new Client({namespace: 'ns', ignore_certs: true, api_key: 'aaa', api: 'my_host', apigw_token: 'oauth_token', apigw_space_guid: 'space_guid'})
t.is(client.options.api, 'my_host')
t.true(client.options.ignore_certs)
t.is(client.options.namespace, 'ns')
+ t.is(client.options.apigw_token, 'oauth_token')
+ t.is(client.options.apigw_space_guid, 'space_guid')
+})
+
+test('should use uuid from auth key as space guid if apigw_token present', t => {
+ const client = new Client({namespace: 'ns', ignore_certs: true, api_key: 'uuid:pass', api: 'my_host', apigw_token: 'oauth_token'})
+ t.is(client.options.apigw_space_guid, 'uuid')
})
test('should use environment parameters for options if not set explicitly.', t => {
process.env['__OW_API_KEY'] = 'some_user:some_pass'
process.env['__OW_API_HOST'] = 'mywhiskhost'
+ process.env['__OW_APIGW_TOKEN'] = 'my-token'
+ process.env['__OW_APIGW_SPACE_GUID'] = 'my-space'
const client = new Client()
t.is(client.options.api_key, process.env['__OW_API_KEY'])
t.is(client.options.api, 'https://mywhiskhost/api/v1/')
+ t.is(client.options.apigw_token, 'my-token')
+ t.is(client.options.apigw_space_guid, 'my-space')
delete process.env['__OW_API_KEY']
delete process.env['__OW_API_HOST']
+ delete process.env['__OW_APIGW_TOKEN']
+ delete process.env['__OW_APIGW_SPACE_GUID']
})
test('should use options for parameters even if environment parameters are available.', t => {
process.env['__OW_API_KEY'] = 'some_user:some_pass'
process.env['__OW_API_HOST'] = 'mywhiskhost'
- const client = new Client({apihost: 'openwhisk', api_key: 'mykey'})
+ process.env['__OW_APIGW_TOKEN'] = 'my-token'
+ process.env['__OW_APIGW_SPACE_GUID'] = 'my-space'
+ const client = new Client({apihost: 'openwhisk', api_key: 'mykey', apigw_token: 'token', apigw_space_guid: 'guid'})
t.is(client.options.api_key, 'mykey')
t.is(client.options.api, 'https://openwhisk/api/v1/')
+ t.is(client.options.apigw_token, 'token')
+ t.is(client.options.apigw_space_guid, 'guid')
delete process.env['__OW_API_KEY']
delete process.env['__OW_API_HOST']
+ delete process.env['__OW_APIGW_TOKEN']
+ delete process.env['__OW_APIGW_SPACE_GUID']
})
test('should throw error when missing API key option.', t => {
diff --git a/test/unit/routes.test.js b/test/unit/routes.test.js
index 3144e9f..567420f 100644
--- a/test/unit/routes.test.js
+++ b/test/unit/routes.test.js
@@ -3,28 +3,56 @@
const test = require('ava')
const Routes = require('../../lib/routes')
+test('should return experimental api path without api token', t => {
+ const client = { options: {} }
+ const routes = new Routes(client)
+ t.is(routes.routeMgmtApiPath('a'), 'experimental/web/whisk.system/routemgmt/a.json')
+})
+
+test('should return experimental api path with api token', t => {
+ const client = { options: {
+ apigw_token: true
+ }}
+ const routes = new Routes(client)
+ t.is(routes.routeMgmtApiPath('a'), 'web/whisk.system/apimgmt/a.http')
+})
+
test('should list all routes', t => {
t.plan(2)
- const client = {}
- const ns = '_'
+ const client = { options: {} }
client.request = (method, path, options) => {
t.is(method, 'GET')
- t.is(path, `${Routes.routeMgmtApiPath}/getApi.json`)
+ t.is(path, routes.routeMgmtApiPath('getApi'))
}
const routes = new Routes(client)
return routes.list()
})
+test('should list all routes with apigw_token', t => {
+ t.plan(3)
+ const client = { options: {
+ apigw_token: 'token',
+ apigw_space_guid: 'space'
+ } }
+ client.request = (method, path, options) => {
+ t.is(method, 'GET')
+ t.is(path, routes.routeMgmtApiPath('getApi'))
+ t.deepEqual(options.qs, { spaceguid: 'space', accesstoken: 'token'})
+ }
+
+ const routes = new Routes(client)
+ return routes.list()
+})
+
+
test('should list all routes with parameters', t => {
t.plan(3)
- const client = {}
- const ns = '_'
-
+ const client = { options: {} }
const options = {basepath: '/hello', relpath: '/foo/bar', operation: 'GET', limit: 10, skip: 10}
client.request = (method, path, _options) => {
t.is(method, 'GET')
- t.is(path, `${Routes.routeMgmtApiPath}/getApi.json`)
+ t.is(path, routes.routeMgmtApiPath('getApi'))
t.deepEqual(_options.qs, options)
}
@@ -34,13 +62,12 @@
test('should delete a route', t => {
t.plan(3)
- const client = {}
- const ns = '_'
+ const client = { options: {} }
const options = {force: true, basepath: '/hello'}
client.request = (method, path, _options) => {
t.is(method, 'DELETE')
- t.is(path, `${Routes.routeMgmtApiPath}/deleteApi.json`)
+ t.is(path, routes.routeMgmtApiPath('deleteApi'))
t.deepEqual(_options.qs, options)
}
@@ -48,15 +75,31 @@
return routes.delete({basepath: '/hello'})
})
+test('should delete a route with apigw token', t => {
+ t.plan(3)
+ const client = { options: {
+ apigw_token: 'token',
+ apigw_space_guid: 'space'
+ } }
+
+ client.request = (method, path, options) => {
+ t.is(method, 'DELETE')
+ t.is(path, routes.routeMgmtApiPath('deleteApi'))
+ t.deepEqual(options.qs, { basepath: '/hello', force: true, spaceguid: 'space', accesstoken: 'token'})
+ }
+
+ const routes = new Routes(client)
+ return routes.delete({basepath: '/hello'})
+})
+
test('should delete a route with parameters', t => {
t.plan(3)
- const client = {}
- const ns = '_'
+ const client = { options: {} }
const options = {force: true, basepath: '/hello', relpath: '/bar/1', operation: 'GET'}
client.request = (method, path, _options) => {
t.is(method, 'DELETE')
- t.is(path, `${Routes.routeMgmtApiPath}/deleteApi.json`)
+ t.is(path, routes.routeMgmtApiPath('deleteApi'))
t.deepEqual(_options.qs, options)
}
@@ -65,8 +108,7 @@
})
test('delete routes without providing basepath', t => {
- const client = {}
- const ns = '_'
+ const client = { options: {} }
const routes = new Routes(client)
return t.throws(() => { routes.delete() }, /Missing mandatory basepath/)
})
@@ -77,7 +119,6 @@
const api_key = 'username:password'
const client_options = { api_key }
const client = { path_url, options: client_options }
- const ns = '_'
const options = {force: true, basepath: '/hello', relpath: '/bar/1', operation: 'GET'}
const body = {
@@ -98,7 +139,7 @@
client.request = (method, path, _options) => {
t.is(method, 'POST')
- t.is(path, `${Routes.routeMgmtApiPath}/createApi.json`)
+ t.is(path, routes.routeMgmtApiPath('createApi'))
t.deepEqual(_options.body, body)
}
@@ -106,13 +147,117 @@
return routes.create({relpath: '/hello', operation: 'GET', action: 'helloAction'})
})
+test('should create a route with apigw_token', t => {
+ t.plan(4)
+ const path_url = path => `https://openwhisk.ng.bluemix.net/api/v1/${path}`
+ const api_key = 'username:password'
+ const client_options = { api_key, apigw_token: 'token', apigw_space_guid: 'space' }
+ const client = { path_url, options: client_options }
+ const options = {force: true, basepath: '/hello', relpath: '/bar/1', operation: 'GET'}
+
+ const body = {
+ apidoc: {
+ namespace: '_',
+ gatewayBasePath: '/',
+ gatewayPath: '/hello',
+ gatewayMethod: 'GET',
+ id: 'API:_:/',
+ action: {
+ name: 'helloAction',
+ namespace: '_',
+ backendMethod: 'GET',
+ backendUrl: 'https://openwhisk.ng.bluemix.net/api/v1/web/_/default/helloAction.http',
+ authkey: api_key }
+ }
+ }
+
+ client.request = (method, path, _options) => {
+ t.is(method, 'POST')
+ t.is(path, routes.routeMgmtApiPath('createApi'))
+ t.deepEqual(_options.body, body)
+ t.deepEqual(_options.qs, { spaceguid: 'space', accesstoken: 'token'})
+ }
+
+ const routes = new Routes(client)
+ return routes.create({relpath: '/hello', operation: 'GET', action: 'helloAction'})
+})
+
+test('should create a route with apigw_token and action with package', t => {
+ t.plan(4)
+ const path_url = path => `https://openwhisk.ng.bluemix.net/api/v1/${path}`
+ const api_key = 'username:password'
+ const client_options = { api_key, apigw_token: 'token', apigw_space_guid: 'space' }
+ const client = { path_url, options: client_options }
+ const options = {force: true, basepath: '/hello', relpath: '/bar/1', operation: 'GET'}
+
+ const body = {
+ apidoc: {
+ namespace: '_',
+ gatewayBasePath: '/',
+ gatewayPath: '/hello',
+ gatewayMethod: 'GET',
+ id: 'API:_:/',
+ action: {
+ name: 'package/helloAction',
+ namespace: '_',
+ backendMethod: 'GET',
+ backendUrl: 'https://openwhisk.ng.bluemix.net/api/v1/web/_/package/helloAction.http',
+ authkey: api_key }
+ }
+ }
+
+ client.request = (method, path, _options) => {
+ t.is(method, 'POST')
+ t.is(path, routes.routeMgmtApiPath('createApi'))
+ t.deepEqual(_options.body, body)
+ t.deepEqual(_options.qs, { spaceguid: 'space', accesstoken: 'token'})
+ }
+
+ const routes = new Routes(client)
+ return routes.create({relpath: '/hello', operation: 'GET', action: 'package/helloAction'})
+})
+
+test('should create a route with apigw_token and action with package & ns', t => {
+ t.plan(4)
+ const path_url = path => `https://openwhisk.ng.bluemix.net/api/v1/${path}`
+ const api_key = 'username:password'
+ const client_options = { api_key, apigw_token: 'token', apigw_space_guid: 'space' }
+ const client = { path_url, options: client_options }
+ const options = {force: true, basepath: '/hello', relpath: '/bar/1', operation: 'GET'}
+
+ const body = {
+ apidoc: {
+ namespace: '_',
+ gatewayBasePath: '/',
+ gatewayPath: '/hello',
+ gatewayMethod: 'GET',
+ id: 'API:_:/',
+ action: {
+ name: 'package/helloAction',
+ namespace: 'ns',
+ backendMethod: 'GET',
+ backendUrl: 'https://openwhisk.ng.bluemix.net/api/v1/web/ns/package/helloAction.http',
+ authkey: api_key }
+ }
+ }
+
+ client.request = (method, path, _options) => {
+ t.is(method, 'POST')
+ t.is(path, routes.routeMgmtApiPath('createApi'))
+ t.deepEqual(_options.body, body)
+ t.deepEqual(_options.qs, { spaceguid: 'space', accesstoken: 'token'})
+ }
+
+ const routes = new Routes(client)
+ return routes.create({relpath: '/hello', operation: 'GET', action: '/ns/package/helloAction'})
+})
+
test('should create a route using global ns', t => {
t.plan(3)
const path_url = path => `https://openwhisk.ng.bluemix.net/api/v1/${path}`
const api_key = 'username:password'
const client_options = { api_key, namespace: 'global_ns'}
const client = { path_url, options: client_options}
- const ns = '_'
const options = {force: true, basepath: '/hello', relpath: '/bar/1', operation: 'GET'}
const body = {
@@ -133,7 +278,7 @@
client.request = (method, path, _options) => {
t.is(method, 'POST')
- t.is(path, `${Routes.routeMgmtApiPath}/createApi.json`)
+ t.is(path, routes.routeMgmtApiPath('createApi'))
t.deepEqual(_options.body, body)
}
@@ -147,7 +292,6 @@
const api_key = 'username:password'
const client_options = { api_key }
const client = { path_url, options: client_options }
- const ns = '_'
const options = {force: true, basepath: '/hello', relpath: '/bar/1', operation: 'GET'}
const body = {
@@ -168,7 +312,7 @@
client.request = (method, path, _options) => {
t.is(method, 'POST')
- t.is(path, `${Routes.routeMgmtApiPath}/createApi.json`)
+ t.is(path, routes.routeMgmtApiPath('createApi'))
t.deepEqual(_options.body, body)
}
@@ -182,7 +326,6 @@
const api_key = 'username:password'
const client_options = { api_key }
const client = { path_url, options: client_options }
- const ns = '_'
const options = {force: true, basepath: '/hello', relpath: '/bar/1', operation: 'GET'}
const body = {
@@ -203,7 +346,7 @@
client.request = (method, path, _options) => {
t.is(method, 'POST')
- t.is(path, `${Routes.routeMgmtApiPath}/createApi.json`)
+ t.is(path, routes.routeMgmtApiPath('createApi'))
t.deepEqual(_options.body, body)
}
@@ -217,7 +360,6 @@
const api_key = 'username:password'
const client_options = { api_key }
const client = { path_url, options: client_options }
- const ns = '_'
const options = {force: true, basepath: '/hello', relpath: '/bar/1', operation: 'GET'}
const body = {
@@ -238,7 +380,7 @@
client.request = (method, path, _options) => {
t.is(method, 'POST')
- t.is(path, `${Routes.routeMgmtApiPath}/createApi.json`)
+ t.is(path, routes.routeMgmtApiPath('createApi'))
t.deepEqual(_options.body, body)
}
@@ -252,7 +394,6 @@
const api_key = 'username:password'
const client_options = { api_key, namespace: 'global' }
const client = { path_url, options: client_options }
- const ns = '_'
const options = {force: true, basepath: '/hello', relpath: '/bar/1', operation: 'GET'}
const body = {
@@ -273,7 +414,7 @@
client.request = (method, path, _options) => {
t.is(method, 'POST')
- t.is(path, `${Routes.routeMgmtApiPath}/createApi.json`)
+ t.is(path, routes.routeMgmtApiPath('createApi'))
t.deepEqual(_options.body, body)
}