Add support for set-variable in operation-switch (#282)

diff --git a/scripts/lua/lib/utils.lua b/scripts/lua/lib/utils.lua
index 2b3cb37..e9c2977 100644
--- a/scripts/lua/lib/utils.lua
+++ b/scripts/lua/lib/utils.lua
@@ -117,4 +117,28 @@
   return resty_str.to_hex(digest)
 end
 
+--- Return the length of the table
+-- @param table
+function _Utils.tableLength(table)
+  local count = 0
+  for _ in pairs(table) do
+    count = count + 1
+  end
+  return count
+end
+
+--- Create a deep clone of the given table
+-- @param table table to clone
+function _Utils.deepCloneTable(table)
+  local tblRes = {}
+  if type(table) == "table" then
+    for k,v in pairs(table) do
+      tblRes[k] = _Utils.deepCloneTable(v)
+    end
+  else
+    tblRes = table
+  end
+  return tblRes
+end
+
 return _Utils
diff --git a/scripts/lua/management/lib/swagger.lua b/scripts/lua/management/lib/swagger.lua
index 5d5c737..4a63ab6 100644
--- a/scripts/lua/management/lib/swagger.lua
+++ b/scripts/lua/management/lib/swagger.lua
@@ -19,6 +19,7 @@
 -- Module for parsing swagger file
 
 local _M = {}
+local utils = require "lib/utils"
 
 -- Convert passed-in swagger body to valid lua table
 -- @param swagger swagger file to parse
@@ -38,16 +39,34 @@
     for verb, value in pairs(verbObj) do
       decoded.resources[path].operations[verb] = {}
       local verbObj = decoded.resources[path].operations[verb]
+      verbObj.policies = utils.deepCloneTable(policies) or {}
+      verbObj.security = security
       if backends ~= nil then
         local backend = (backends["all"] ~= nil) and backends["all"] or backends[value.operationId]
         verbObj.backendUrl = backend.backendUrl
         verbObj.backendMethod = (backend.backendMethod == 'keep') and verb or backend.backendMethod
+        if backend.policy ~= nil then
+          local globalReqMappingPolicy = nil;
+          for _, policy in pairs(verbObj.policies) do
+            if policy.type == 'reqMapping' then
+              globalReqMappingPolicy = policy;
+            end
+          end
+          if globalReqMappingPolicy ~= nil then
+            for _, v in pairs(backend.policy.value) do
+              globalReqMappingPolicy.value[#globalReqMappingPolicy.value+1] = v
+            end
+          else
+            verbObj.policies[#verbObj.policies+1] = {
+              type = 'reqMapping',
+              value = backend.policy.value
+            }
+          end
+        end
       else
         verbObj.backendUrl = ''
         verbObj.backendMethod = verb
       end
-      verbObj.policies = policies
-      verbObj.security = security
     end
   end
   return decoded
@@ -66,10 +85,18 @@
           local caseObj = v.case
           for _, case in pairs(caseObj) do
             for _, op in pairs(case.operations) do
-              res[op] = {
-                backendUrl = case.execute[1]["invoke"]["target-url"],
-                backendMethod = case.execute[1]["invoke"].verb
-              }
+              res[op] = {}
+              for _, opPolicy in pairs(case.execute) do
+                if opPolicy.invoke ~= nil then
+                  res[op].backendUrl = opPolicy.invoke["target-url"]
+                  res[op].backendMethod = opPolicy.invoke.verb
+                elseif opPolicy["set-variable"] ~= nil then
+                  local reqMappingPolicy = parseRequestMapping(case)
+                  if reqMappingPolicy ~= nil then
+                    res[op].policy = reqMappingPolicy
+                  end
+                end
+              end
             end
           end
           return res
@@ -91,17 +118,27 @@
 function parsePolicies(swagger)
   local policies = {}
   -- parse rate limit
-  policies = parseRateLimit(swagger, policies)
-  policies = parseRequestMapping(swagger, policies)
+  local rlObj = swagger["x-gateway-rate-limit"]
+  rlObj = (rlObj == nil) and swagger["x-ibm-rate-limit"] or rlObj
+  local rateLimitPolicy = parseRateLimit(rlObj)
+  if rateLimitPolicy ~= nil then
+    policies[#policies+1] = rateLimitPolicy
+  end
+  -- parse set-variable
+  local configObj = swagger["x-gateway-configuration"]
+  configObj = (configObj == nil) and swagger["x-ibm-configuration"] or configObj
+  if configObj ~= nil then
+    local reqMappingPolicy = parseRequestMapping(configObj.assembly)
+    if reqMappingPolicy ~= nil then
+      policies[#policies+1] = reqMappingPolicy
+    end
+  end
   return policies
 end
 
 --- Parse rate limit
-function parseRateLimit(swagger, policies)
-  local rlObj = swagger["x-gateway-rate-limit"]
-  rlObj = (rlObj == nil) and swagger["x-ibm-rate-limit"] or rlObj
-  local unit
-  if rlObj ~= nil then
+function parseRateLimit(rlObj)
+  if rlObj ~= nil and rlObj[1] ~= nil then
     rlObj = rlObj[1]
     if rlObj.unit == "second" then
       unit = 1
@@ -114,7 +151,7 @@
     else
       unit = 60   -- default to minute
     end
-    policies[#policies+1] = {
+    return {
       type = "rateLimit",
       value = {
         interval = unit * rlObj.units,
@@ -124,14 +161,14 @@
       }
     }
   end
-  return policies
+  return nil
 end
 
 --- Parse request mapping
-function parseRequestMapping(swagger, policies)
+function parseRequestMapping(configObj)
   local valueList = {}
-  if swagger["x-ibm-configuration"] ~= nil then
-    for _, obj in pairs(swagger["x-ibm-configuration"].assembly.execute) do
+  if configObj ~= nil then
+    for _, obj in pairs(configObj.execute) do
       for policy, v in pairs(obj) do
         if policy == "set-variable" then
           for _, actionObj in pairs(v.actions) do
@@ -156,12 +193,13 @@
     end
   end
   if next(valueList) ~= nil then
-    policies[#policies+1] ={
+    return {
       type = "reqMapping",
       value = valueList
     }
+  else
+    return nil
   end
-  return policies
 end
 
 --- Parse security in swagger
@@ -170,19 +208,34 @@
   local security = {}
   if swagger["securityDefinitions"] ~= nil then
     local secObject = swagger["securityDefinitions"]
-    for key, sec in pairs(secObject) do
-      if sec.type == 'apiKey' then
-        security[#security+1] = {
-          type = sec.type,
-          scope = "api",
-          header = sec.name
-        }
-      elseif sec.type == 'oauth2' then
-        security[#security+1] = {
-          type = sec.type,
-          scope = "api",
-          provider = key
-        }
+    if utils.tableLength(secObject) == 2 then
+      secObj = {
+        type = 'clientSecret',
+        scope = 'api'
+      }
+      for key, sec in pairs(secObject) do
+        if key == 'client_id' then
+          secObj.idFieldName = sec.name
+        elseif key == 'client_secret' then
+          secObj.secretFieldName = sec.name
+        end
+      end
+      security[#security+1] = secObj
+    else
+      for key, sec in pairs(secObject) do
+        if sec.type == 'apiKey' then
+          security[#security+1] = {
+            type = sec.type,
+            scope = "api",
+            header = sec.name
+          }
+        elseif sec.type == 'oauth2' then
+          security[#security+1] = {
+            type = sec.type,
+            scope = "api",
+            provider = key
+          }
+        end
       end
     end
   end
diff --git a/tests/scripts/lua/management/examples/example2.json b/tests/scripts/lua/management/examples/example2.json
index 47794c6..db02482 100644
--- a/tests/scripts/lua/management/examples/example2.json
+++ b/tests/scripts/lua/management/examples/example2.json
@@ -62,7 +62,7 @@
       "rate": 100
     }
   ],
-  "x-ibm-configuration": {
+  "x-gateway-configuration": {
     "assembly": {
       "execute": [
         {
@@ -84,6 +84,16 @@
                 ],
                 "execute": [
                   {
+                    "set-variable": {
+                      "actions": [
+                        {
+                          "set": "message.headers.foo",
+                          "value": "bar"
+                        }
+                      ]
+                    }
+                  },
+                  {
                     "invoke": {
                       "target-url": "https://openwhisk.ng.bluemix.net/api/some/action/path.http",
                       "verb": "keep"
diff --git a/tests/scripts/lua/management/examples/example3.json b/tests/scripts/lua/management/examples/example3.json
new file mode 100644
index 0000000..bd3b344
--- /dev/null
+++ b/tests/scripts/lua/management/examples/example3.json
@@ -0,0 +1,102 @@
+{
+  "swagger": "2.0",
+  "info": {
+    "version": "1.0",
+    "title": "Hello World API"
+  },
+  "basePath": "/whisk2",
+  "schemes": [
+    "https"
+  ],
+  "consumes": [
+    "application/json"
+  ],
+  "produces": [
+    "application/json"
+  ],
+  "paths": {
+    "/bye": {
+      "get": {
+        "operationId": "getBye",
+        "description": "Returns a greeting to the user!",
+        "responses": {
+          "200": {
+            "description": "Returns the greeting.",
+            "schema": {
+              "type": "string"
+            }
+          },
+          "400": {
+            "description": "Invalid characters in \"user\" were provided."
+          }
+        }
+      },
+      "post": {
+        "operationId": "postBye",
+        "description": null
+      }
+    }
+  },
+  "securityDefinitions": {},
+  "security": [],
+  "x-ibm-rate-limit": [],
+  "x-gateway-configuration": {
+    "assembly": {
+      "execute": [
+        {
+          "operation-switch": {
+            "case": [
+              {
+                "operations": [
+                  "getBye"
+                ],
+                "execute": [
+                  {
+                    "set-variable": {
+                      "actions": [
+                        {
+                          "set": "message.headers.foo",
+                          "value": "bar"
+                        }
+                      ]
+                    }
+                  },
+                  {
+                    "set-variable": {
+                      "actions": [
+                        {
+                          "set": "message.headers.hello",
+                          "value": "world"
+                        }
+                      ]
+                    }
+                  },
+                  {
+                    "invoke": {
+                      "target-url": "https://openwhisk.ng.bluemix.net/api/some/action/path.http",
+                      "verb": "keep"
+                    }
+                  }
+                ]
+              },
+              {
+                "operations": [
+                  "postBye"
+                ],
+                "execute": [
+                  {
+                    "invoke": {
+                      "target-url": "https://openwhisk.ng.bluemix.net/api/user@us.ibm.com/demo/createuser",
+                      "verb": "keep"
+                    }
+                  }
+                ]
+              }
+            ],
+            "otherwise": []
+          }
+        }
+      ]
+    }
+  }
+}
diff --git a/tests/scripts/lua/management/lib/swagger.lua b/tests/scripts/lua/management/lib/swagger.lua
index cf9a565..0860667 100644
--- a/tests/scripts/lua/management/lib/swagger.lua
+++ b/tests/scripts/lua/management/lib/swagger.lua
@@ -80,7 +80,7 @@
                       "type": "rateLimit",
                       "value": {
                         "scope": "api",
-                        "subscription": "true",
+                        "subscription": true,
                         "interval": 180,
                         "rate": 100
                       }
@@ -116,35 +116,43 @@
                       "type": "rateLimit",
                       "value": {
                         "scope": "api",
-                        "subscription": "true",
+                        "subscription": true,
                         "interval": 180,
                         "rate": 100
                       }
                     },
                     {
                       "type": "reqMapping",
-                      "value": [{
-                        "from": {
-                          "value": "Basic xxx"
+                      "value": [
+                        {
+                          "from": {
+                            "value": "Basic xxx"
+                          },
+                          "to": {
+                            "name": "Authorization",
+                            "location": "header"
+                          },
+                          "action": "insert"
                         },
-                        "to": {
-                          "name": "Authorization",
-                          "location": "header"
-                        },
-                        "action": "insert"
-                      }]
+                        {
+                          "from": {
+                            "value": "bar"
+                          },
+                          "to": {
+                            "name": "foo",
+                            "location": "header"
+                          },
+                          "action": "insert"
+                        }
+                      ]
                     }
                   ],
                   "security": [
                     {
-                      "type": "oauth2",
-                      "provider": "google",
-                      "scope": "api"
-                    },
-                    {
-                      "type": "apiKey",
-                      "header": "X-Api-Key",
-                      "scope": "api"
+                      "type": "clientSecret",
+                      "scope": "api",
+                      "idFieldName":"X-Api-Key",
+                      "secretFieldName":"X-Api-Secret"
                     }
                   ],
                   "backendMethod": "get"
@@ -157,7 +165,64 @@
     local jsonPath = exampleBasePath .. 'example2.json'
     local jsonTable = loadJsonTable(jsonPath)
     local actual = swagger.parseSwagger(jsonTable)
-    assert.are.same(expected.resources["/hello"].operations.post.policies[2].value, actual.resources["/hello"].operations.post.policies[2].value)
+    assert.are.same(expected, actual)
+  end)
+
+  it('should parse set-variable policy within operation-switch correctly', function()
+    local expected = cjson.decode([[
+        {
+          "basePath": "/whisk2",
+          "name": "Hello World API",
+          "resources": {
+            "/bye": {
+              "operations": {
+                "post": {
+                  "backendUrl": "https://openwhisk.ng.bluemix.net/api/user@us.ibm.com/demo/createuser",
+                  "policies": [],
+                  "security": [],
+                  "backendMethod": "post"
+                },
+                "get": {
+                  "backendUrl": "https://openwhisk.ng.bluemix.net/api/some/action/path.http",
+                  "policies": [
+                    {
+                      "type": "reqMapping",
+                      "value": [
+                        {
+                          "from": {
+                            "value": "bar"
+                          },
+                          "to": {
+                            "name": "foo",
+                            "location": "header"
+                          },
+                          "action": "insert"
+                        },
+                        {
+                          "from": {
+                            "value": "world"
+                          },
+                          "to": {
+                            "name": "hello",
+                            "location": "header"
+                          },
+                          "action": "insert"
+                        }
+                      ]
+                    }
+                  ],
+                  "security": [],
+                  "backendMethod": "get"
+                }
+              }
+            }
+          }
+        }
+      ]])
+    local jsonPath = exampleBasePath .. 'example3.json'
+    local jsonTable = loadJsonTable(jsonPath)
+    local actual = swagger.parseSwagger(jsonTable)
+    assert.are.same(expected, actual)
   end)
 end)