Modify rest interface for adding resources
diff --git a/api-gateway-config/conf.d/management_apis.conf b/api-gateway-config/conf.d/management_apis.conf
index dc8c714..92af22d 100644
--- a/api-gateway-config/conf.d/management_apis.conf
+++ b/api-gateway-config/conf.d/management_apis.conf
@@ -33,14 +33,14 @@
     access_log /var/log/api-gateway/access.log platform;
     error_log /var/log/api-gateway/mgmt_error.log debug;
 
-    location /resources {
+    location /APIs {
         access_by_lua_block {
             local mgmt = require("management")
             local requestMethod = ngx.req.get_method()
             if requestMethod == "GET" then
                 mgmt.getResource()
             elseif requestMethod == "PUT" then
-                mgmt.addResource()
+                mgmt.addAPI()
             elseif requestMethod == "POST" then
                 ngx.status = 400
                 ngx.say("Use PUT")
diff --git a/api-gateway-config/scripts/lua/lib/filemgmt.lua b/api-gateway-config/scripts/lua/lib/filemgmt.lua
index 25c9cfe..9a50e3f 100644
--- a/api-gateway-config/scripts/lua/lib/filemgmt.lua
+++ b/api-gateway-config/scripts/lua/lib/filemgmt.lua
@@ -49,7 +49,6 @@
   end
   -- Add CORS headers
   prefix = utils.concatStrings({prefix, "\tadd_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS';\n"})
-
   -- Set resource headers and mapping by calling routing.processCall()
   local outgoingResource = utils.concatStrings({
     "\taccess_by_lua_block {\n",
diff --git a/api-gateway-config/scripts/lua/lib/redis.lua b/api-gateway-config/scripts/lua/lib/redis.lua
index f8144fc..1369c7a 100644
--- a/api-gateway-config/scripts/lua/lib/redis.lua
+++ b/api-gateway-config/scripts/lua/lib/redis.lua
@@ -79,43 +79,29 @@
 end
 
 --- Generate Redis object for resource
--- @param red redis client instance
--- @param key redis resource key
--- @param gatewayMethod resource gateway method
--- @param backendUrl resource backend url
--- @param backendMethod resource backend method
+-- @param ops list of operations for a given resource
 -- @param apiId resource api id (nil if no api)
--- @param policies list of policy objects
--- @param security security object
-function _M.generateResourceObj(red, key, gatewayMethod, backendUrl, backendMethod, apiId, policies, security)
-  local newResource
-  local resourceObj = _M.getResource(red, key, REDIS_FIELD)
-  if resourceObj == nil then
-    newResource = {
-      operations = {
-        [gatewayMethod] = {
-          backendUrl = backendUrl,
-          backendMethod = backendMethod,
-        }
-      }
+function _M.generateResourceObj(ops, apiId)
+  local resourceObj = {
+    operations = {}
+  }
+  for op, v in pairs(ops) do
+    op = op:upper()
+    resourceObj.operations[op] = {
+      backendUrl = v.backendUrl,
+      backendMethod = v.backendMethod
     }
-  else
-    newResource = cjson.decode(resourceObj)
-    newResource.operations[gatewayMethod] = {
-      backendUrl = backendUrl,
-      backendMethod = backendMethod,
-    }
+    if v.policies then
+      resourceObj.operations[op].policies = v.policies
+    end
+    if v.security then
+      resourceObj.operations[op].security = v.security
+    end
   end
   if apiId then
-    newResource.apiId = apiId
+    resourceObj.apiId = apiId
   end
-  if policies then
-    newResource.operations[gatewayMethod].policies = policies
-  end
-  if security then
-    newResource.operations[gatewayMethod].security = security
-  end
-  return cjson.encode(newResource)
+  return cjson.encode(resourceObj)
 end
 
 --- Create/update resource in redis
@@ -316,4 +302,4 @@
   ngx.exit(ngx.status)
 end
 
-return _M
+return _M
\ No newline at end of file
diff --git a/api-gateway-config/scripts/lua/management.lua b/api-gateway-config/scripts/lua/management.lua
index 929fbd2..194fabd 100644
--- a/api-gateway-config/scripts/lua/management.lua
+++ b/api-gateway-config/scripts/lua/management.lua
@@ -28,76 +28,147 @@
 local utils = require "lib/utils"
 local logger = require "lib/logger"
 local request = require "lib/request"
-
 local REDIS_HOST = os.getenv("REDIS_HOST")
 local REDIS_PORT = os.getenv("REDIS_PORT")
 local REDIS_PASS = os.getenv("REDIS_PASS")
-
 local REDIS_FIELD = "resources"
-
 local BASE_CONF_DIR = "/etc/api-gateway/managed_confs/"
 
 local _M = {}
 
 --- Add/update a resource to redis and create/update an nginx conf file given PUT JSON body
--- PUT http://0.0.0.0:9000/resources/<tenant>/<url-encoded-resource>
--- Example PUT JSON body:
+-- PUT http://0.0.0.0:9000/APIs
+-- PUT JSON body:
 -- {
---      "api": "12345"
---      "gatewayMethod": "GET",
---      "backendURL": "http://openwhisk.ng.bluemix.net/guest/action?blocking=true",
---      "backendMethod": "POST",
---      "policies": [],
---      "security": {
---        "type": "apikey"
---      }
---  }
-function _M.addResource()
+--    "name": *(String) name of API
+--    "basePath": *(String) base path for api
+--    "tenantId": *(String) tenant id
+--    "resources": *(String) resources to add
+-- }
+function _M.addAPI()
   -- Read in the PUT JSON Body
   ngx.req.read_body()
   local args = ngx.req.get_body_data()
   if not args then
-    request.err(400, "Missing Request body")
+    request.err(400, "Missing request body")
   end
   -- Convert json into Lua table
   local decoded = cjson.decode(args)
-  -- Error handling for required fields in the request body
-  local gatewayMethod = decoded.gatewayMethod
-  if not gatewayMethod then
-    request.err(400, "\"gatewayMethod\" missing from request body.")
+  -- Error checking
+  local fields = {"name", "basePath", "tenantId", "resources"}
+  for k, v in pairs(fields) do
+    local res, err = isValid(v, decoded[v])
+    if res == false then
+      request.err(err.statusCode, err.message)
+    end
   end
-  local backendUrl = decoded.backendURL
-  if not backendUrl then
-    request.err(400, "\"backendURL\" missing from request body.")
-  end
-  -- Use gatewayMethod by default or usebackendMethod if specified
-  local backendMethod = decoded and decoded.backendMethod or gatewayMethod
-  -- apiId, policies, security fields are optional
-  local apiId = decoded.apiId
-  -- TODO: Error handling needed for policies and security
-  local policies = decoded.policies
-  local security = decoded.security
-  local requestURI = string.gsub(ngx.var.request_uri, "?.*", "")
-  local list = parseRequestURI(requestURI)
-  local tenant = list[2]
-  local gatewayPath = list[3]
-  local redisKey = utils.concatStrings({"resources", ":", tenant, ":", ngx.unescape_uri(gatewayPath)})
   -- Open connection to redis or use one from connection pool
   local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
-  local resourceObj = redis.generateResourceObj(red, redisKey, gatewayMethod, backendUrl, backendMethod, apiId, policies, security)
-  redis.createResource(red, redisKey, REDIS_FIELD, resourceObj)
-  filemgmt.createResourceConf(BASE_CONF_DIR, tenant, gatewayPath, resourceObj)
+  -- Format basePath
+  local basePath = decoded.basePath:sub(1,1) == '/' and decoded.basePath:sub(2) or decoded.basePath
+  -- Add resources to redis and create nginx conf files
+  for path, resource in pairs(decoded.resources) do
+    local gatewayPath = utils.concatStrings({basePath, ngx.escape_uri(path)})
+    addResource(red, resource, gatewayPath, decoded.tenantId)
+  end
   -- Add current redis connection in the ngx_lua cosocket connection pool
   redis.close(red)
   -- Return managed url object
   local managedUrlObj = {
-    managedUrl = utils.concatStrings({"http://0.0.0.0:8080/api/", tenant, "/", gatewayPath})
+    name = decoded.name,
+    basePath = utils.concatStrings({"/", basePath}),
+    tenantId = decoded.tenantId,
+    resources = decoded.resources,
+    managedUrl = utils.concatStrings({"http://0.0.0.0:8080/api/", decoded.tenantId, "/", basePath})
   }
   managedUrlObj = cjson.encode(managedUrlObj):gsub("\\", "")
   ngx.header.content_type = "application/json; charset=utf-8"
   request.success(200, managedUrlObj)
 end
 
+--- Check JSON body fields for errors
+-- @param field name of field
+-- @param object field object
+function isValid(field, object)
+  -- Check that field exists in body
+  if not object then
+    return false, { statusCode = 400, message = utils.concatStrings({"Missing field '", field, "' in request body."}) }
+  end
+  -- Additional checks for resource object
+  if field == "resources" then
+    local resources = object
+    if next(object) == nil then
+      return false, { statusCode = 400, message = "Empty resources object." }
+    end
+    for path, resource in pairs(resources) do
+      -- Check that resource path begins with slash
+      if path:sub(1,1) ~= '/' then
+        return false, { statusCode = 400, message = "Resource path must begin with '/'." }
+      end
+      -- Check operations object
+      if not resource.operations or next(resource.operations) == nil then
+        return false, { statusCode = 400, message = "Missing or empty field 'operations' or in resource path object." }
+      end
+      for verb, verbObj in pairs(resource.operations) do
+        local allowedVerbs = {GET=true, POST=true, PUT=true, DELETE=true, PATCH=true, HEAD=true, OPTIONS=true}
+        if allowedVerbs[verb:upper()] == nil then
+          return false, { statusCode = 400, message = utils.concatStrings({"Resource verb '", verb, "' not supported."}) }
+        end
+        -- Check required fields
+        local requiredFields = {"backendMethod", "backendUrl"}
+        for k, v in pairs(requiredFields) do
+          if verbObj[v] == nil then
+            return false, { statusCode = 400, message = utils.concatStrings({"Missing field '", v, "' for '", verb, "' operation."}) }
+          end
+          if v == "backendMethod" then
+            local backendMethod = verbObj[v]
+            if allowedVerbs[backendMethod:upper()] == nil then
+              return false, { statusCode = 400, message = utils.concatStrings({"backendMethod '", backendMethod, "' not supported."}) }
+            end
+          end
+        end
+        -- Check optional fields
+        local policies = verbObj.policies
+        if policies then
+          for k, v in pairs(policies) do
+            if v.type == nil then
+              return false, { statusCode = 400, message = "Missing field 'type' in policy object." }
+            end
+          end
+        end
+        local security = verbObj.security
+        if security and security.type == nil then
+          return false, { statusCode = 400, message = "Missing field 'type' in security object." }
+        end
+      end
+    end
+  end
+  -- All error checks passed
+  return true
+end
+
+--- Helper function for adding a resource to redis and creating an nginx conf file
+-- @param red
+-- @param resource
+-- @param gatewayPath
+-- @param tenantId
+function addResource(red, resource, gatewayPath, tenantId)
+  -- Create resource object and add to redis
+  local redisKey = utils.concatStrings({"resources", ":", tenantId, ":", ngx.unescape_uri(gatewayPath)})
+  local apiId
+  local operations
+  for k, v in pairs(resource) do
+    if k == 'apiId' then
+      apiId = v
+    elseif k == 'operations' then
+      operations = v
+    end
+  end
+  local resourceObj = redis.generateResourceObj(operations, apiId)
+  redis.createResource(red, redisKey, REDIS_FIELD, resourceObj)
+  filemgmt.createResourceConf(BASE_CONF_DIR, tenantId, gatewayPath, resourceObj)
+end
+
 --- Get resource from redis
 -- GET http://0.0.0.0:9000/resources/<tenant>/<url-encoded-resource>
 function _M.getResource()
@@ -276,4 +347,4 @@
   return list  --prefix, tenant, gatewayPath, apiKey
 end
 
-return _M
\ No newline at end of file
+return _M
diff --git a/api-gateway-config/scripts/lua/routing.lua b/api-gateway-config/scripts/lua/routing.lua
index fa62b71..0ed8f65 100644
--- a/api-gateway-config/scripts/lua/routing.lua
+++ b/api-gateway-config/scripts/lua/routing.lua
@@ -48,7 +48,7 @@
     if string.upper(verb) == ngx.req.get_method() then
       -- Check if auth is required
       local apiKey
-      if (opFields.security and string.lower(opFields.security.type) == 'apikey') then
+      if (opFields.security and opFields.security.type ~= nil and string.lower(opFields.security.type) == 'apikey') then
         apiKey = security.processAPIKey(opFields.security)
       end
       -- Parse backend url
diff --git a/api-gateway-config/tests/spec/test.lua b/api-gateway-config/tests/spec/test.lua
index 04da8e7..b3e88a0 100644
--- a/api-gateway-config/tests/spec/test.lua
+++ b/api-gateway-config/tests/spec/test.lua
@@ -121,24 +121,19 @@
 
   it('should generate resource object to store in redis', function()
     -- Resource object with no policies or security
-    local key = 'resources:guest:hello'
-    local gatewayMethod = 'GET'
-    local backendUrl = 'https://httpbin.org/get'
-    local backendMethod = gatewayMethod
     local apiId = 12345
-    local policies
-    local security
+    local operations = {
+      GET = {
+        backendUrl = 'https://httpbin.org/get',
+        backendMethod = 'GET'
+      }
+    }
     local resourceObj = {
-      operations = {
-        [gatewayMethod] = {
-          backendUrl = backendUrl,
-          backendMethod = backendMethod
-        }
-      },
-      apiId = apiId
+      apiId = apiId,
+      operations = operations
     }
     local expected = resourceObj
-    local generated = cjson.decode(redis.generateResourceObj(red, key, gatewayMethod, backendUrl, backendMethod, apiId, policies, security))
+    local generated = cjson.decode(redis.generateResourceObj(operations, apiId))
     assert.are.same(expected, generated)
 
     -- Resource object with policy added
@@ -153,10 +148,9 @@
           }]
       }]
     ]]
-    policies = cjson.decode(policyList)
-    resourceObj.operations[gatewayMethod].policies = policies
+    resourceObj.operations.GET.policies = cjson.decode(policyList)
     expected = resourceObj
-    generated = cjson.decode(redis.generateResourceObj(red, key, gatewayMethod, backendUrl, backendMethod, apiId, policies, security))
+    generated = cjson.decode(redis.generateResourceObj(operations, apiId))
     assert.are.same(expected, generated)
 
     -- Resource object with security added
@@ -167,24 +161,19 @@
         "header":"myheader"
       }
     ]]
-    security = cjson.decode(securityObj)
-    resourceObj.operations[gatewayMethod].security = security
+    resourceObj.operations.GET.security = cjson.decode(securityObj)
     expected = resourceObj
-    generated = cjson.decode(redis.generateResourceObj(red, key, gatewayMethod, backendUrl, backendMethod, apiId, policies, security))
+    generated = cjson.decode(redis.generateResourceObj(operations, apiId))
     assert.are.same(expected, generated)
 
-    -- Update already existing resource object
-    local field = 'resources'
-    redis.createResource(red, key, field, cjson.encode(generated))
-    local newGatewayMethod = 'POST'
-    resourceObj.operations[newGatewayMethod] = {
-      backendUrl = backendUrl,
-      backendMethod = backendMethod
+    -- Resource object with multiple operations
+    resourceObj.operations.PUT = {
+        backendUrl = 'https://httpbin.org/get',
+        backendMethod = 'PUT',
+        security = {}
     }
-    policies = nil
-    security = nil
     expected = resourceObj
-    generated = cjson.decode(redis.generateResourceObj(red, key, newGatewayMethod, backendUrl, backendMethod, apiId, policies, security))
+    generated = cjson.decode(redis.generateResourceObj(operations, apiId))
     assert.are.same(expected, generated)
   end)