Implement API CRUD operations
diff --git a/api-gateway-config/conf.d/management_apis.conf b/api-gateway-config/conf.d/management_apis.conf
index 92af22d..0d9e30d 100644
--- a/api-gateway-config/conf.d/management_apis.conf
+++ b/api-gateway-config/conf.d/management_apis.conf
@@ -38,14 +38,13 @@
local mgmt = require("management")
local requestMethod = ngx.req.get_method()
if requestMethod == "GET" then
- mgmt.getResource()
+ mgmt.getAPIs()
elseif requestMethod == "PUT" then
mgmt.addAPI()
elseif requestMethod == "POST" then
- ngx.status = 400
- ngx.say("Use PUT")
+ mgmt.addAPI()
elseif requestMethod == "DELETE" then
- mgmt.deleteResource()
+ mgmt.deleteAPI()
else
ngx.status = 400
ngx.say("Invalid verb")
diff --git a/api-gateway-config/scripts/lua/lib/redis.lua b/api-gateway-config/scripts/lua/lib/redis.lua
index 1369c7a..7ea41b2 100644
--- a/api-gateway-config/scripts/lua/lib/redis.lua
+++ b/api-gateway-config/scripts/lua/lib/redis.lua
@@ -33,6 +33,10 @@
local _M = {}
+----------------------------
+-- Initialization/Cleanup --
+----------------------------
+
--- Initialize and connect to Redis
-- @param host redis host
-- @param port redis port
@@ -78,6 +82,59 @@
end
end
+---------------------------
+----------- APIs ----------
+---------------------------
+
+--- Add API to redis
+-- @param red Redis client instance
+-- @param id id of API
+-- @param apiObj the api to add
+function _M.addAPI(red, id, apiObj)
+ local ok, err = red:hset("apis", id, apiObj)
+ if not ok then
+ request.err(500, utils.concatStrings({"Failed adding API to redis: ", err}))
+ end
+end
+
+--- Get all APIs from redis
+-- @param red Redis client instance
+function _M.getAllAPIs(red)
+ local res, err = red:hgetall("apis")
+ if not res then
+ request.err(500, utils.concatStrings({"Failed getting APIs from redis: ", err}))
+ end
+ return res
+end
+
+--- Get a single API from redis given its id
+-- @param redis Redis client instance
+-- @param id id of API to get
+function _M.getAPI(red, id)
+ local api, err = red:hget("apis", id)
+ if not api then
+ request.err(500, utils.concatStrings({"Failed getting API from redis: ", err}))
+ end
+ if api == ngx.null then
+ request.err(404, utils.concatStrings({"Unknown API id ", id, "."}))
+ end
+ return cjson.decode(api)
+end
+
+--- Delete an API from redis given its id
+-- @param redis Redis client instance
+-- @param id id of API to delete
+function _M.deleteAPI(red, id)
+ local ok, err = red:hdel("apis", id)
+ if not ok then
+ request.err(500, utils.concatStrings({"Failed deleting API from redis: ", err}))
+ end
+end
+
+-----------------------------
+--------- Resources ---------
+-----------------------------
+
--- Generate Redis object for resource
-- @param ops list of operations for a given resource
-- @param apiId resource api id (nil if no api)
@@ -155,6 +212,10 @@
end
end
+-----------------------------
+--- API Key Subscriptions ---
+-----------------------------
+
--- Create/update subscription/apikey in redis
-- @param red redis client instance
-- @param key redis subscription key to create
@@ -183,6 +244,10 @@
end
end
+-----------------------------------
+------- Pub/Sub with Redis --------
+-----------------------------------
+
--- Subscribe to redis
-- @param redisSubClient the redis client that is listening for the redis key changes
-- @param redisGetClient the redis client that gets the changed resource to update the conf file
diff --git a/api-gateway-config/scripts/lua/lib/utils.lua b/api-gateway-config/scripts/lua/lib/utils.lua
index fe49819..003b74d 100644
--- a/api-gateway-config/scripts/lua/lib/utils.lua
+++ b/api-gateway-config/scripts/lua/lib/utils.lua
@@ -75,9 +75,20 @@
return concatStrings({"(?<path_" , x , ">([a-zA-Z0-9\\-\\s\\_\\%]*))"})
end
+--- Generate random uuid
+function uuid()
+ local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
+ math.randomseed(os.time())
+ return string.gsub(template, '[xy]', function (c)
+ local v = (c == 'x') and math.random(0, 0xf) or math.random(8, 0xb)
+ return string.format('%x', v)
+ end)
+end
+
_Utils.concatStrings = concatStrings
_Utils.serializeTable = serializeTable
_Utils.convertTemplatedPathParam = convertTemplatedPathParam
+_Utils.uuid = uuid
return _Utils
diff --git a/api-gateway-config/scripts/lua/management.lua b/api-gateway-config/scripts/lua/management.lua
index 194fabd..8cd3a65 100644
--- a/api-gateway-config/scripts/lua/management.lua
+++ b/api-gateway-config/scripts/lua/management.lua
@@ -36,7 +36,11 @@
local _M = {}
---- Add/update a resource to redis and create/update an nginx conf file given PUT JSON body
+--------------------------
+---------- APIs ----------
+--------------------------
+
+--- Add an api to the Gateway
-- PUT http://0.0.0.0:9000/APIs
-- PUT JSON body:
-- {
@@ -71,10 +75,11 @@
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
+ -- Generate random uuid for api
+ local uuid = utils.uuid()
+ -- Create managed url object
local managedUrlObj = {
+ id = uuid,
name = decoded.name,
basePath = utils.concatStrings({"/", basePath}),
tenantId = decoded.tenantId,
@@ -82,6 +87,11 @@
managedUrl = utils.concatStrings({"http://0.0.0.0:8080/api/", decoded.tenantId, "/", basePath})
}
managedUrlObj = cjson.encode(managedUrlObj):gsub("\\", "")
+ -- Add API object to redis
+ redis.addAPI(red, uuid, managedUrlObj)
+ -- Add current redis connection in the ngx_lua cosocket connection pool
+ redis.close(red)
+ -- Return managed url object
ngx.header.content_type = "application/json; charset=utf-8"
request.success(200, managedUrlObj)
end
@@ -169,57 +179,85 @@
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()
- local requestURI = string.gsub(ngx.var.request_uri, "?.*", "")
- local list = parseRequestURI(requestURI)
- local tenant = list[2]
- local gatewayPath = list[3]
- local redisKey = utils.concatStrings({list[1], ":", tenant, ":", ngx.unescape_uri(gatewayPath)})
- -- Initialize and connect to redis
- local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
- local resourceObj = redis.getResource(red, redisKey, REDIS_FIELD)
- if resourceObj == nil then
- request.err(404, "Resource doesn't exist.")
+--- Get one or all APIs from the gateway
+-- PUT http://0.0.0.0:9000/APIs
+function _M.getAPIs()
+ local uri = string.gsub(ngx.var.request_uri, "?.*", "")
+ if uri:len() <= 6 then
+ getAllAPIs()
+ else
+ local id = uri:sub(7)
+ getAPI(id)
end
- -- Add current redis connection in the ngx_lua cosocket connection pool
- redis.close(red)
- -- Get available operations for the given resource
- resourceObj = cjson.decode(resourceObj)
- local operations = {}
- for k in pairs(resourceObj.operations) do
- operations[#operations+1] = k
- end
- -- Return managed url object
- local managedUrlObj = {
- managedUrl = utils.concatStrings({"http://0.0.0.0:8080/api/", tenant, "/", gatewayPath}),
- availableOperations = operations
- }
- managedUrlObj = cjson.encode(managedUrlObj):gsub("\\", "")
- ngx.header.content_type = "application/json; charset=utf-8"
- request.success(200, managedUrlObj)
end
---- Delete resource from redis
--- DELETE http://0.0.0.0:9000/resources/<tenant>/<url-encoded-resource>
-function _M.deleteResource()
- local requestURI = string.gsub(ngx.var.request_uri, "?.*", "")
- local list = parseRequestURI(requestURI)
- local tenant = list[2]
- local gatewayPath = list[3]
- local redisKey = utils.concatStrings({list[1], ":", tenant, ":", ngx.unescape_uri(gatewayPath)})
- -- Initialize and connect to redis
+--- Get all APIs in redis
+function getAllAPIs()
local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
- -- Return if resource doesn't exist
- redis.deleteResource(red, redisKey, REDIS_FIELD)
- -- Delete conf file
- filemgmt.deleteResourceConf(BASE_CONF_DIR, tenant, gatewayPath)
- -- Add current redis connection in the ngx_lua cosocket connection pool
+ local res = redis.getAllAPIs(red)
redis.close(red)
- request.success(200, "Resource deleted.")
+ local apiList = {}
+ for k, v in pairs(res) do
+ if k%2 == 0 then
+ apiList[#apiList+1] = cjson.decode(v)
+ end
+ end
+ apiList = cjson.encode(apiList)
+ ngx.header.content_type = "application/json; charset=utf-8"
+ request.success(200, apiList)
end
+--- Get API by its id
+-- @param id
+function getAPI(id)
+ local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
+ local api = redis.getAPI(red, id)
+ redis.close(red)
+ ngx.header.content_type = "application/json; charset=utf-8"
+ request.success(200, cjson.encode(api))
+end
+
+--- Delete API from gateway
+-- DELETE http://0.0.0.0:9000/APIs/<id>
+function _M.deleteAPI()
+ local uri = string.gsub(ngx.var.request_uri, "?.*", "")
+ if uri:len() <= 6 then
+ request.err(400, "No id specified.")
+ end
+ local id = uri:sub(7)
+ local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
+ local api = redis.getAPI(red, id)
+ -- Delete all resources for the API
+ local basePath = api.basePath:sub(2)
+ for path, v in pairs(api.resources) do
+ local gatewayPath = utils.concatStrings({basePath, ngx.escape_uri(path)})
+ deleteResource(red, gatewayPath, api.tenantId)
+ end
+ redis.deleteAPI(red, id)
+ redis.close(red)
+ request.success(200, {})
+end
+
+--- Helper function for deleting resource in redis and appropriate conf files
+-- @param red redis instance
+-- @param gatewayPath path in gateway
+-- @param tenantId tenant id
+function deleteResource(red, gatewayPath, tenantId)
+ local redisKey = utils.concatStrings({"resources:", tenantId, ":", ngx.unescape_uri(gatewayPath)})
+ redis.deleteResource(red, redisKey, REDIS_FIELD)
+ filemgmt.deleteResourceConf(BASE_CONF_DIR, tenantId, gatewayPath)
+end
+
+-----------------------------
+---------- Tenants ----------
+-----------------------------
+
+
+
+------------------------------
+----- Pub/Sub with Redis -----
+------------------------------
+
--- Subscribe to redis
-- GET http://0.0.0.0:9000/subscribe
function _M.subscribe()
@@ -261,6 +299,10 @@
request.success(200, "Subscription created.")
end
+---------------------------
+------ Subscriptions ------
+---------------------------
+
--- Delete apikey/subscription from redis
-- DELETE http://0.0.0.0:9000/subscriptions
-- Body: