From 8ad9bbbe17afc17b76390265f5ae38dedcd13c0a Mon Sep 17 00:00:00 2001 From: Alex Song Date: Mon, 12 Mar 2018 21:30:59 -0400 Subject: [PATCH] Add support for set-variable in operation-switch (#282) --- scripts/lua/lib/utils.lua | 24 ++++ scripts/lua/management/lib/swagger.lua | 119 +++++++++++++----- .../lua/management/examples/example2.json | 12 +- .../lua/management/examples/example3.json | 102 +++++++++++++++ tests/scripts/lua/management/lib/swagger.lua | 105 +++++++++++++--- 5 files changed, 308 insertions(+), 54 deletions(-) create mode 100644 tests/scripts/lua/management/examples/example3.json diff --git a/scripts/lua/lib/utils.lua b/scripts/lua/lib/utils.lua index 2b3cb377..e9c2977c 100644 --- a/scripts/lua/lib/utils.lua +++ b/scripts/lua/lib/utils.lua @@ -117,4 +117,28 @@ function _Utils.hash(str) 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 5d5c737c..4a63ab69 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 @@ function _M.parseSwagger(swagger) 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 @@ function parseBackends(swagger) 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 @@ end 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 @@ function parseRateLimit(swagger, policies) else unit = 60 -- default to minute end - policies[#policies+1] = { + return { type = "rateLimit", value = { interval = unit * rlObj.units, @@ -124,14 +161,14 @@ function parseRateLimit(swagger, policies) } } 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 @@ function parseRequestMapping(swagger, policies) 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 @@ function parseSecurity(swagger) 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 47794c69..db02482c 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": [ { @@ -83,6 +83,16 @@ "getHello" ], "execute": [ + { + "set-variable": { + "actions": [ + { + "set": "message.headers.foo", + "value": "bar" + } + ] + } + }, { "invoke": { "target-url": "https://openwhisk.ng.bluemix.net/api/some/action/path.http", diff --git a/tests/scripts/lua/management/examples/example3.json b/tests/scripts/lua/management/examples/example3.json new file mode 100644 index 00000000..bd3b3448 --- /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 cf9a5657..08606677 100644 --- a/tests/scripts/lua/management/lib/swagger.lua +++ b/tests/scripts/lua/management/lib/swagger.lua @@ -80,7 +80,7 @@ describe('Testing v2 management interface', function() "type": "rateLimit", "value": { "scope": "api", - "subscription": "true", + "subscription": true, "interval": 180, "rate": 100 } @@ -116,35 +116,43 @@ describe('Testing v2 management interface', function() "type": "rateLimit", "value": { "scope": "api", - "subscription": "true", + "subscription": true, "interval": 180, "rate": 100 } }, { "type": "reqMapping", - "value": [{ - "from": { - "value": "Basic xxx" - }, - "to": { - "name": "Authorization", - "location": "header" + "value": [ + { + "from": { + "value": "Basic xxx" + }, + "to": { + "name": "Authorization", + "location": "header" + }, + "action": "insert" }, - "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 @@ describe('Testing v2 management interface', function() 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)