Add new features to routes methods. (#85)

* Remove "experimental" API Gateway support.
* Adding supporting for `responsetype` for route creation.
* Updated Routes with api name, retrieval and swagger support
Fixes #52
Fixes #69 
Fixes #70 
Fixes #71
diff --git a/README.md b/README.md
index 057e276..afd150e 100644
--- a/README.md
+++ b/README.md
@@ -273,6 +273,7 @@
 ow.triggers.delete({name: '...'})
 ow.rules.delete({name: '...'})
 ow.packages.delete({name: '...'})
+ow.feeds.delete({name: '...', trigger: '...'})
 ```
 
 The following optional parameters are supported:
@@ -414,11 +415,10 @@
 The following optional parameters are supported:
 - `namespace` - set custom namespace for endpoint
 
-### create & delete feeds
+### create feeds
 
 ```javascript
 ow.feeds.create({feedName: '...', trigger: '...'})
-ow.feeds.delete({feedName: '...', trigger: '...'})
 ```
 
 The following optional parameters are supported:
@@ -433,6 +433,15 @@
 
 *The interface for managing routes through the library does not change between providers.*
 
+### retrieve route
+
+```javascript
+ow.routes.get({basepath: '...'})
+ow.routes.get({name: '...'})
+```
+
+*This method is a wrapper for the list method. It throws an error if the base path or name parameter is missing.*
+
 ### list routes
 
 ```javascript
@@ -442,16 +451,18 @@
 The following optional parameters are supported to filter the result set:
 - `relpath` - relative URI path for endpoints
 - `basepath` - base URI path for endpoints
+- `name` - identifier for API
 - `operation` - HTTP methods
 - `limit` - limit result set size
 - `skip` - skip results from index
 
-*`relpath` is only valid when `basepath` is also specified.*
+*`relpath` is only valid when `basepath` is also specified. `name` and `basepath` cannot be used together.*
 
 ### delete routes
 
 ```javascript
 ow.routes.delete({basepath: '...'})
+ow.routes.delete({name: '...'})
 ```
 
 The following optional parameters are supported to filter the result set:
@@ -468,6 +479,17 @@
 The following optional parameters are supported:
 - `responsetype` - content type returned by web action, possible values: `html`, `http`, `json`, `text` and `svg` (default: `json`).
 - `basepath` - base URI path for endpoints (default: `/`)
+- `name` - identifier for API (default: `basepath`)
+
+### add route (swagger)
+
+```javascript
+ow.routes.create({swagger: '{...}'})
+```
+
+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).
+
+*No other parameters are supported when creating the route from a JSON Swagger document.*
 
 ## Debugging
 
@@ -511,7 +533,7 @@
 Run the script with openwhisk credentials:  
 ```bash
 $ ./test/integration/prepIntegrationTests.sh <your key in the form of ABCD:EFGH> <openwhisk instance hostname> <openwhisk namespace> <api gatewaytoken>
-```  
+```
 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`
 
 ## Code-Coverage:
diff --git a/lib/messages.js b/lib/messages.js
index 909721f..a1873f9 100644
--- a/lib/messages.js
+++ b/lib/messages.js
@@ -18,5 +18,6 @@
   MISSING_PACKAGE_BODY_ERROR: 'Missing mandatory package parameter from options.',
   MISSING_NAMESPACE_ERROR: 'Missing namespace from options, please set a default namespace or pass one in the options.',
   INVALID_OPTIONS_ERROR: 'Invalid constructor options.',
-  MISSING_BASEPATH_ERROR: 'Missing mandatory basepath parameter from options.'
+  MISSING_BASEPATH_ERROR: 'Missing mandatory parameters: basepath or name.',
+  INVALID_BASEPATH_ERROR: 'Invalid parameters: use basepath or name, not both.'
 }
diff --git a/lib/routes.js b/lib/routes.js
index edb5f60..eb55026 100644
--- a/lib/routes.js
+++ b/lib/routes.js
@@ -13,8 +13,19 @@
     return `web/whisk.system/apimgmt/${path}.http`
   }
 
+  get (options) {
+    options = options || {}
+    options.basepath = this.basepath(options)
+
+    return this.list(this.qs(options, ['basepath']))
+  }
+
   list (options) {
     options = options || {}
+    if (this.has_basepath(options)) {
+      options.basepath = this.calculate_basepath(options)
+    }
+
     const qs = this.qs(options, ['relpath', 'basepath', 'operation', 'limit', 'skip'])
     return this.client.request('GET', this.routeMgmtApiPath('getApi'), { qs })
   }
@@ -30,26 +41,57 @@
     return result
   }
 
-  delete (options) {
-    if (!options || !options.hasOwnProperty('basepath')) {
+  has_basepath (options) {
+    return !!(options.name || options.basepath)
+  }
+
+  basepath (options) {
+    if (!this.has_basepath(options)) {
       throw new Error(messages.MISSING_BASEPATH_ERROR)
     }
 
+    return this.calculate_basepath(options)
+  }
+
+  calculate_basepath (options) {
+    if (options.name && options.basepath) {
+      throw new Error(messages.INVALID_BASEPATH_ERROR)
+    }
+
+    return options.basepath || options.name
+  }
+
+  missing_basepath (options) {
+    return !(options.name || options.basepath)
+  }
+
+  delete (options) {
+    options = options || {}
+    options.basepath = this.basepath(options)
+
     const qs = this.qs(options, ['relpath', 'basepath', 'operation'])
     qs.force = true
     return this.client.request('DELETE', this.routeMgmtApiPath('deleteApi'), { qs })
   }
 
   create (options) {
+    const body = this.create_body(options || {})
+    const qs = this.qs(options, ['responsetype'])
+    return this.client.request('POST', this.routeMgmtApiPath('createApi'), { body, qs })
+  }
+
+  create_body (options) {
+    if (options.swagger) {
+      return { apidoc: { namespace: '_', swagger: options.swagger } }
+    }
+
     const missing = CREATE_PARAMS.filter(param => !(options || {}).hasOwnProperty(param))
 
     if (missing.length) {
       throw new Error(`Missing mandatory parameters: ${missing.join(', ')}`)
     }
 
-    const body = this.route_swagger_definition(options)
-    const qs = this.qs(options, ['responsetype'])
-    return this.client.request('POST', this.routeMgmtApiPath('createApi'), { body, qs })
+    return this.route_swagger_definition(options)
   }
 
   route_swagger_definition (params) {
@@ -60,7 +102,11 @@
       gatewayMethod: params.operation,
       id: `API:_:${this.route_base_path(params)}`,
       action: this.route_swagger_action(params)
-   }
+    }
+
+    if (params.name) {
+      apidoc.apiName = params.name
+    }
 
     return { apidoc }
   }
diff --git a/test/unit/routes.test.js b/test/unit/routes.test.js
index 0d25c36..9d039e4 100644
--- a/test/unit/routes.test.js
+++ b/test/unit/routes.test.js
@@ -6,6 +6,73 @@
 const test = require('ava')
 const Routes = require('../../lib/routes')
 
+test('should retrieve routes from name', t => {
+  t.plan(3)
+  const client = { options: {} }
+  client.request = (method, path, options) => {
+    t.is(method, 'GET')
+    t.is(path, routes.routeMgmtApiPath('getApi'))
+    t.deepEqual(options.qs, { basepath: 'testing' })
+  }
+
+  const routes = new Routes(client)
+  return routes.get({name: 'testing'})
+})
+
+test('should retrieve routes from basepath', t => {
+  t.plan(3)
+  const client = { options: {} }
+  client.request = (method, path, options) => {
+    t.is(method, 'GET')
+    t.is(path, routes.routeMgmtApiPath('getApi'))
+    t.deepEqual(options.qs, { basepath: 'testing' })
+  }
+
+  const routes = new Routes(client)
+  return routes.get({basepath: 'testing'})
+})
+
+test('should retrieve routes with apigw_token and name', 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, { basepath: 'testing', spaceguid: 'space', accesstoken: 'token'})
+  }
+
+  const routes = new Routes(client)
+  return routes.get({name: 'testing'})
+})
+
+test('should retrieve routes with apigw_token and basepath', 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, { basepath: 'testing', spaceguid: 'space', accesstoken: 'token'})
+  }
+
+  const routes = new Routes(client)
+  return routes.get({basepath: 'testing'})
+})
+
+test('get routes with incorrect parameters', t => {
+  const routes = new Routes({options: {}})
+  t.throws(() => { routes.get() }, /Missing mandatory parameters: basepath or name/)
+  t.throws(() => { routes.get({}) }, /Missing mandatory parameters: basepath or name/)
+  t.throws(() => { routes.get({basepath: 'id', name: 'id'}) }, /Invalid parameters: use basepath or name, not both/)
+})
+
+// ADD NAME TO OTHER METHODS
+
 test('should list all routes', t => {
   t.plan(2)
   const client = { options: {} }
@@ -35,7 +102,7 @@
 })
 
 
-test('should list all routes with parameters', t => {
+test('should list all routes with parameters including basepath', t => {
   t.plan(3)
   const client = { options: {} }
   const options = {basepath: '/hello', relpath: '/foo/bar', operation: 'GET', limit: 10, skip: 10}
@@ -49,7 +116,28 @@
   return routes.list(options)
 })
 
-test('should delete a route', t => {
+test('should list all routes with parameters including name', t => {
+  t.plan(3)
+  const client = { options: {} }
+  const options = {name: '/hello', relpath: '/foo/bar', operation: 'GET', limit: 10, skip: 10}
+  const qs_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'))
+    t.deepEqual(_options.qs, qs_options)
+  }
+
+  const routes = new Routes(client)
+  return routes.list(options)
+})
+
+test('list routes providing basepath and name', t => {
+  const client = { options: {} }
+  const routes = new Routes(client)
+  return t.throws(() => { routes.list({basepath: 'bp', name: 'nm'}) }, /Invalid parameters: use basepath or name, not both/)
+})
+
+test('should delete a route with basepath', t => {
   t.plan(3)
   const client = { options: {} }
   const options = {force: true, basepath: '/hello'}
@@ -64,6 +152,21 @@
   return routes.delete({basepath: '/hello'})
 })
 
+test('should delete a route with name', t => {
+  t.plan(3)
+  const client = { options: {} }
+  const options = {force: true, basepath: '/hello'}
+
+  client.request = (method, path, _options) => {
+    t.is(method, 'DELETE')
+    t.is(path, routes.routeMgmtApiPath('deleteApi'))
+    t.deepEqual(_options.qs, options)
+  }
+
+  const routes = new Routes(client)
+  return routes.delete({name: '/hello'})
+})
+
 test('should delete a route with apigw token', t => {
   t.plan(3)
   const client = { options: {
@@ -96,10 +199,16 @@
   return routes.delete({basepath: '/hello', relpath: '/bar/1', operation: 'GET'})
 })
 
-test('delete routes without providing basepath', t => {
+test('delete routes without providing basepath or name', t => {
   const client = { options: {} }
   const routes = new Routes(client)
-  return t.throws(() => { routes.delete() }, /Missing mandatory basepath/)
+  return t.throws(() => { routes.delete() }, /Missing mandatory parameters: basepath or name/)
+})
+
+test('delete routes providing basepath and name', t => {
+  const client = { options: {} }
+  const routes = new Routes(client)
+  return t.throws(() => { routes.delete({basepath: 'bp', name: 'nm'}) }, /Invalid parameters: use basepath or name, not both/)
 })
 
 test('should create a route', t => {
@@ -136,6 +245,65 @@
   return routes.create({relpath: '/hello', operation: 'GET', action: 'helloAction'})
 })
 
+test('should create a route from swagger file', 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 }
+  const client = { path_url, options: client_options }
+
+  const body = {
+    apidoc: {
+      namespace: '_',
+      swagger: '{"hello": "world"}'
+    }
+  }
+
+  client.request = (method, path, _options) => {
+    t.is(method, 'POST')
+    t.is(path, routes.routeMgmtApiPath('createApi'))
+    t.deepEqual(_options.body, body)
+  }
+
+  const routes = new Routes(client)
+  return routes.create({swagger: '{"hello": "world"}'})
+})
+
+test('should create a route with api name', 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 }
+  const client = { path_url, options: client_options }
+  const options = {force: true, basepath: '/hello', relpath: '/bar/1', operation: 'GET'}
+
+  const body = {
+    apidoc: {
+      namespace: '_',
+      apiName: 'SOME_NAME',
+      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)
+  }
+
+  const routes = new Routes(client)
+  return routes.create({relpath: '/hello', operation: 'GET', action: 'helloAction', name: 'SOME_NAME'})
+})
+
 test('should create a route with apigw_token', t => {
   t.plan(4)
   const path_url = path => `https://openwhisk.ng.bluemix.net/api/v1/${path}`
@@ -446,7 +614,6 @@
   return routes.create({relpath: '/hello', operation: 'GET', action: '/test/helloAction'})
 })
 
-
 test('create routes missing mandatory parameters', t => {
   const routes = new Routes()
   t.throws(() => { routes.create() }, /Missing mandatory parameters: relpath, operation, action/)