Allowing apihost options parameters fixes #14
diff --git a/lib/base_operation.js b/lib/base_operation.js
index aa83f5e..855937b 100644
--- a/lib/base_operation.js
+++ b/lib/base_operation.js
@@ -7,9 +7,29 @@
 
 class BaseOperation {
   constructor (options) {
+    this.validate_options(options)
     this.options = options
   }
 
+  validate_options (options) {
+    if (!options.hasOwnProperty('api_key')) {
+      throw new Error(`${messages.INVALID_OPTIONS_ERROR} Missing api_key parameter.`)
+    }
+    if (!options.hasOwnProperty('api')) {
+      if (!options.hasOwnProperty('apihost')) {
+        throw new Error(`${messages.INVALID_OPTIONS_ERROR} Missing either api or apihost parameters.`)
+      }
+
+      options.api = this.url_from_apihost(options.apihost)
+    }
+  }
+
+  url_from_apihost (apihost) {
+    const is_http = apihost.includes(':') && !apihost.includes(':443')
+    const protocol = is_http ? 'http' : 'https'
+    return `${protocol}://${apihost}/api/v1/`
+  }
+
   request (options) {
     return rp(options).catch(err => this.handle_errors(err))
   }
diff --git a/lib/messages.js b/lib/messages.js
index ffd2759..69e5e3c 100644
--- a/lib/messages.js
+++ b/lib/messages.js
@@ -16,5 +16,6 @@
   CREATE_CONFLICT_ERROR: 'Conflict detected updating resource.',
   INVALID_AUTH_ERROR: 'OpenWhisk authentication failed, check API key?',
   MISSING_URL_ERROR: 'Invalid URL for API called, OpenWhisk returned HTTP 404 response.',
-  API_SYSTEM_ERROR: 'API call failed, response contained error code.'
+  API_SYSTEM_ERROR: 'API call failed, response contained error code.',
+  INVALID_OPTIONS_ERROR: 'Invalid constructor options.'
 }
diff --git a/test/unit/base_operation.test.js b/test/unit/base_operation.test.js
index c13d0ce..8ed2dd1 100644
--- a/test/unit/base_operation.test.js
+++ b/test/unit/base_operation.test.js
@@ -4,7 +4,7 @@
 const BaseOperation = require('../../lib/base_operation')
 
 test('should throw errors for HTTP response failures', t => {
-  const base_operation = new BaseOperation()
+  const base_operation = new BaseOperation({api_key: true, api: true})
   t.throws(() => base_operation.handle_errors({statusCode: 401}), /authentication failed/)
   t.throws(() => base_operation.handle_errors({statusCode: 403}), /authentication failed/)
   t.throws(() => base_operation.handle_errors({statusCode: 404}), /HTTP 404/)
@@ -14,19 +14,45 @@
   t.throws(() => base_operation.handle_errors({statusCode: 500, error: {response: {result: {error: 'custom'}}}}), /custom/)
 })
 
+test('should throw error when missing API key option.', t => {
+  t.throws(() => new BaseOperation({api: true}), /Missing api_key parameter./)
+})
+
+test('should throw error when missing both API and API Host options.', t => {
+  t.throws(() => new BaseOperation({api_key: true}), /Missing either api or apihost parameters/)
+})
+
+test('should set options.api from options.apihost with hostname only', t => {
+  const base_operation = new BaseOperation({api_key: true, apihost: 'my_host'})
+  t.true(base_operation.options.hasOwnProperty('api'))
+  t.is(base_operation.options.api, 'https://my_host/api/v1/')
+})
+
+test('should set options.api from options.apihost with hostname and https port', t => {
+  const base_operation = new BaseOperation({api_key: true, apihost: 'my_host:443'})
+  t.true(base_operation.options.hasOwnProperty('api'))
+  t.is(base_operation.options.api, 'https://my_host:443/api/v1/')
+})
+
+test('should set options.api from options.apihost with hostname and https port', t => {
+  const base_operation = new BaseOperation({api_key: true, apihost: 'my_host:80'})
+  t.true(base_operation.options.hasOwnProperty('api'))
+  t.is(base_operation.options.api, 'http://my_host:80/api/v1/')
+})
+
 test('should throw errors for non-HTTP response failures', t => {
-  const base_operation = new BaseOperation()
+  const base_operation = new BaseOperation({api_key: true, api: true})
   t.throws(() => base_operation.handle_errors({message: 'error message'}), /error message/)
 })
 
 test('should generate auth header from API key', t => {
   const api_key = 'some sample api key'
-  const base_operation = new BaseOperation({api_key: api_key})
+  const base_operation = new BaseOperation({api: true, api_key: api_key})
   t.is(base_operation.auth_header(), `Basic ${new Buffer(api_key).toString('base64')}`)
 })
 
 test('should extract available query string parameters', t => {
-  const base_operation = new BaseOperation()
+  const base_operation = new BaseOperation({api_key: true, api: true})
   t.deepEqual(base_operation.qs({}, ['a', 'b', 'c']), {})
   t.deepEqual(base_operation.qs({a: 1}, ['a', 'b', 'c']), {a: 1})
   t.deepEqual(base_operation.qs({a: 1, c: 2}, ['a', 'b', 'c']), {a: 1, c: 2})
@@ -34,19 +60,19 @@
 })
 
 test('should return provided namespace', t => {
-  let base_operation = new BaseOperation()
+  let base_operation = new BaseOperation({api_key: true, api: true})
   t.is(base_operation.namespace({namespace: 'provided'}), 'provided')
-  base_operation = new BaseOperation({namespace: 'default'})
+  base_operation = new BaseOperation({api_key: true, api: true, namespace: 'default'})
   t.is(base_operation.namespace({namespace: 'provided'}), 'provided')
 })
 
 test('should return default namespace', t => {
-  const base_operation = new BaseOperation({namespace: 'default'})
+  const base_operation = new BaseOperation({api_key: true, api: true, namespace: 'default'})
   t.is(base_operation.namespace(), 'default')
 })
 
 test('should throw for missing namespace', t => {
-  const base_operation = new BaseOperation({})
+  const base_operation = new BaseOperation({api_key: true, api: true})
   t.throws(() => base_operation.namespace({}), /Missing namespace/)
   t.throws(() => base_operation.namespace({namespace: null}), /Missing namespace/)
   t.throws(() => base_operation.namespace({namespace: undefined}), /Missing namespace/)
@@ -71,7 +97,7 @@
 })
 
 test('should url encode namespace parameter', t => {
-  const options = {namespace: 'sample@path'}
+  const options = {api_key: true, api: true, namespace: 'sample@path'}
   let base_operation = new BaseOperation(options)
 
   t.is(base_operation.namespace(), `sample%40path`)