Add query relationships between tenants and APIs
diff --git a/api-gateway-config/conf.d/management_apis.conf b/api-gateway-config/conf.d/management_apis.conf
index 47f8ee8..eccdcb1 100644
--- a/api-gateway-config/conf.d/management_apis.conf
+++ b/api-gateway-config/conf.d/management_apis.conf
@@ -33,7 +33,7 @@
access_log /var/log/api-gateway/access.log platform;
error_log /var/log/api-gateway/mgmt_error.log debug;
- location /APIs {
+ location /v1/apis {
access_by_lua_block {
local mgmt = require("management")
local requestMethod = ngx.req.get_method()
@@ -52,7 +52,7 @@
}
}
- location /Tenants {
+ location /v1/tenants {
access_by_lua_block {
local mgmt = require("management")
local requestMethod = ngx.req.get_method()
diff --git a/api-gateway-config/scripts/lua/lib/redis.lua b/api-gateway-config/scripts/lua/lib/redis.lua
index 2687248..62b2322 100644
--- a/api-gateway-config/scripts/lua/lib/redis.lua
+++ b/api-gateway-config/scripts/lua/lib/redis.lua
@@ -93,7 +93,7 @@
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}))
+ request.err(500, utils.concatStrings({"Failed to save the API: ", err}))
end
end
@@ -102,32 +102,32 @@
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}))
+ request.err(500, utils.concatStrings({"Failed to retrieve APIs: ", err}))
end
return res
end
--- Get a single API from redis given its id
--- @param redis Redis client instance
+-- @param red 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}))
+ request.err(500, utils.concatStrings({"Failed to retrieve the API: ", err}))
end
if api == ngx.null then
- request.err(404, utils.concatStrings({"Unknown API id ", id, "."}))
+ return nil
end
return cjson.decode(api)
end
--- Delete an API from redis given its id
--- @param redis Redis client instance
+-- @param red 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}))
+ request.err(500, utils.concatStrings({"Failed to delete the API: ", err}))
end
end
@@ -170,7 +170,7 @@
-- Add/update resource to redis
local ok, err = red:hset(key, field, resourceObj)
if not ok then
- request.err(500, utils.concatStrings({"Failed adding resource to redis: ", err}))
+ request.err(500, utils.concatStrings({"Failed to save the resource: ", err}))
end
end
@@ -182,7 +182,7 @@
function _M.getResource(red, key, field)
local resourceObj, err = red:hget(key, field)
if not resourceObj then
- request.err(500, utils.concatStrings({"Failed getting resource: ", err}))
+ request.err(500, utils.concatStrings({"Failed to retrieve the resource: ", err}))
end
-- return nil if resource doesn't exist
if resourceObj == ngx.null then
@@ -199,14 +199,14 @@
function _M.deleteResource(red, key, field)
local resourceObj, err = red:hget(key, field)
if not resourceObj then
- request.err(500, utils.concatStrings({"Failed deleting resource: ", err}))
+ request.err(500, utils.concatStrings({"Failed to delete the resource: ", err}))
end
if resourceObj == ngx.null then
request.err(404, "Resource doesn't exist.")
end
local ok, err = red:del(key)
if not ok then
- request.err(500, utils.concatStrings({"Failed deleting resource: ", err}))
+ request.err(500, utils.concatStrings({"Failed to delete the resource: ", err}))
else
return ok
end
@@ -223,7 +223,7 @@
function _M.addTenant(red, id, tenantObj)
local ok, err = red:hset("tenants", id, tenantObj)
if not ok then
- request.err(500, utils.concatStrings({"Failed adding tenant to redis: ", err}))
+ request.err(500, utils.concatStrings({"Failed to add the tenant: ", err}))
end
end
@@ -232,32 +232,32 @@
function _M.getAllTenants(red)
local res, err = red:hgetall("tenants")
if not res then
- request.err(500, utils.concatStrings({"Failed getting tenants from redis: ", err}))
+ request.err(500, utils.concatStrings({"Failed to retrieve tenants: ", err}))
end
return res
end
--- Get a single tenant from redis given its id
--- @param redis Redis client instance
+-- @param red Redis client instance
-- @param id id of tenant to get
function _M.getTenant(red, id)
local tenant, err = red:hget("tenants", id)
if not tenant then
- request.err(500, utils.concatStrings({"Failed getting tenants from redis: ", err}))
+ request.err(500, utils.concatStrings({"Failed to retrieve the tenant: ", err}))
end
if tenant == ngx.null then
- request.err(404, utils.concatStrings({"Unknown tenant id ", id, "."}))
+ return nil
end
return cjson.decode(tenant)
end
--- Delete an tenant from redis given its id
--- @param redis Redis client instance
+-- @param red Redis client instance
-- @param id id of tenant to delete
function _M.deleteTenant(red, id)
local ok, err = red:hdel("tenants", id)
if not ok then
- request.err(500, utils.concatStrings({"Failed deleting tenant from redis: ", err}))
+ request.err(500, utils.concatStrings({"Failed to delete the tenant: ", err}))
end
end
@@ -272,7 +272,7 @@
-- Add/update a subscription key to redis
local ok, err = red:set(key, '')
if not ok then
- request.err(500, utils.concatStrings({"Failed adding subscription to redis", err}))
+ request.err(500, utils.concatStrings({"Failed to add the subscription key", err}))
end
end
@@ -282,14 +282,14 @@
function _M.deleteSubscription(red, key)
local subscription, err = red:get(key)
if not subscription then
- request.err(500, utils.concatStrings({"Failed to delete subscription: ", err}))
+ request.err(500, utils.concatStrings({"Failed to delete the subscription key: ", err}))
end
if subscription == ngx.null then
request.err(404, "Subscription doesn't exist.")
end
local ok, err = red:del(key)
if not ok then
- request.err(500, utils.concatStrings({"Failed to delete subscription: ", err}))
+ request.err(500, utils.concatStrings({"Failed to delete the subscription key: ", err}))
end
end
diff --git a/api-gateway-config/scripts/lua/management.lua b/api-gateway-config/scripts/lua/management.lua
index 6648007..0bc3357 100644
--- a/api-gateway-config/scripts/lua/management.lua
+++ b/api-gateway-config/scripts/lua/management.lua
@@ -50,6 +50,24 @@
-- "resources": *(String) resources to add
-- }
function _M.addAPI()
+ -- Open connection to redis or use one from connection pool
+ local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
+ -- Check for api id and use existingAPI if it already exists in redis
+ local uri = string.gsub(ngx.var.request_uri, "?.*", "")
+ local apiId, existingAPI
+ local index = 1
+ for word in string.gmatch(uri, '([^/]+)') do
+ if index == 3 then
+ apiId = word
+ end
+ index = index + 1
+ end
+ if apiId ~= nil then
+ existingAPI = redis.getAPI(red, apiId)
+ if existingAPI == nil then
+ request.err(404, utils.concatStrings({"Unknown API id ", apiId}))
+ end
+ end
-- Read in the PUT JSON Body
ngx.req.read_body()
local args = ngx.req.get_body_data()
@@ -61,13 +79,11 @@
-- Error checking
local fields = {"name", "basePath", "tenantId", "resources"}
for k, v in pairs(fields) do
- local res, err = isValid(v, decoded[v])
+ local res, err = isValid(red, v, decoded[v])
if res == false then
request.err(err.statusCode, err.message)
end
end
- -- Open connection to redis or use one from connection pool
- local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
-- 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
@@ -75,9 +91,8 @@
local gatewayPath = utils.concatStrings({basePath, ngx.escape_uri(path)})
addResource(red, resource, gatewayPath, decoded.tenantId)
end
- -- Generate random uuid for api
- local uuid = utils.uuid()
- -- Create managed url object
+ -- Return managedUrl object
+ local uuid = existingAPI ~= nil and existingAPI.id or utils.uuid()
local managedUrlObj = {
id = uuid,
name = decoded.name,
@@ -89,7 +104,6 @@
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"
@@ -97,13 +111,21 @@
end
--- Check JSON body fields for errors
+-- @param red Redis client instance
-- @param field name of field
-- @param object field object
-function isValid(field, object)
+function isValid(red, 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 check f or tenantId
+ if field == "tenantId" then
+ local tenant = redis.getTenant(red, object)
+ if tenant == nil then
+ return false, { statusCode = 404, message = utils.concatStrings({"Unknown tenant id ", object }) }
+ end
+ end
-- Additional checks for resource object
if field == "resources" then
local resources = object
@@ -180,14 +202,32 @@
end
--- Get one or all APIs from the gateway
--- PUT http://0.0.0.0:9000/APIs
+-- GET http://0.0.0.0:9000/APIs
function _M.getAPIs()
local uri = string.gsub(ngx.var.request_uri, "?.*", "")
- if uri:len() <= 6 then
+ local id
+ local index = 1
+ local tenantQuery = false
+ for word in string.gmatch(uri, '([^/]+)') do
+ if index == 3 then
+ id = word
+ elseif index == 4 then
+ if word == 'tenant' then
+ tenantQuery = true
+ else
+ request.err(400, "Invalid request")
+ end
+ end
+ index = index + 1
+ end
+ if id == nil then
getAllAPIs()
else
- local id = uri:sub(7)
- getAPI(id)
+ if tenantQuery == false then
+ getAPI(id)
+ else
+ getAPITenant(id)
+ end
end
end
@@ -208,25 +248,56 @@
end
--- Get API by its id
--- @param id
+-- @param id of API
function getAPI(id)
local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
local api = redis.getAPI(red, id)
+ if api == nil then
+ request.err(404, utils.concatStrings({"Unknown api id ", id}))
+ end
redis.close(red)
ngx.header.content_type = "application/json; charset=utf-8"
request.success(200, cjson.encode(api))
end
+--- Get belongsTo relation tenant
+-- @param id id of API
+function getAPITenant(id)
+ local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
+ local api = redis.getAPI(red, id)
+ if api == nil then
+ request.err(404, utils.concatStrings({"Unknown api id ", id}))
+ end
+ local tenantId = api.tenantId
+ local tenant = redis.getTenant(red, tenantId)
+ if tenant == nil then
+ request.err(404, utils.concatStrings({"Unknown tenant id ", tenantId}))
+ end
+ redis.close(red)
+ ngx.header.content_type = "application/json; charset=utf-8"
+ request.success(200, cjson.encode(tenant))
+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
+ local index = 1
+ local id
+ for word in string.gmatch(uri, '([^/]+)') do
+ if index == 3 then
+ id = word
+ end
+ index = index + 1
+ end
+ if id == nil 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)
+ if api == nil then
+ request.err(404, utils.concatStrings({"Unknown API id ", id}))
+ end
-- Delete all resources for the API
local basePath = api.basePath:sub(2)
for path, v in pairs(api.resources) do
@@ -260,6 +331,24 @@
-- "instance": *(String) tenant instance
-- }
function _M.addTenant()
+ -- Open connection to redis or use one from connection pool
+ local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
+ -- Check for tenant id and use existingTenant if it already exists in redis
+ local uri = string.gsub(ngx.var.request_uri, "?.*", "")
+ local tenantId, existingTenant
+ local index = 1
+ for word in string.gmatch(uri, '([^/]+)') do
+ if index == 3 then
+ tenantId = word
+ end
+ index = index + 1
+ end
+ if tenantId ~= nil then
+ existingTenant = redis.getTenant(red, tenantId)
+ if existingTenant == nil then
+ request.err(400, utils.concatStrings({"Unknown tenant id ", tenantId}))
+ end
+ end
-- Read in the PUT JSON Body
ngx.req.read_body()
local args = ngx.req.get_body_data()
@@ -275,15 +364,14 @@
request.err(400, utils.concatStrings({"Missing field '", v, "' in request body."}))
end
end
- -- Generate random uuid for tenant
- local uuid = utils.uuid()
+ -- Return tenant object
+ local uuid = existingTenant ~= nil and existingTenant.id or utils.uuid()
local tenantObj = {
id = uuid,
namespace = decoded.namespace,
instance = decoded.instance
}
tenantObj = cjson.encode(tenantObj)
- local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
redis.addTenant(red, uuid, tenantObj)
redis.close(red)
ngx.header.content_type = "application/json; charset=utf-8"
@@ -291,14 +379,32 @@
end
--- Get one or all tenants from the gateway
--- PUT http://0.0.0.0:9000/Tenants
+-- GET http://0.0.0.0:9000/Tenants
function _M.getTenants()
local uri = string.gsub(ngx.var.request_uri, "?.*", "")
- if uri:len() <= 9 then
+ local id
+ local index = 1
+ local apiQuery = false
+ for word in string.gmatch(uri, '([^/]+)') do
+ if index == 3 then
+ id = word
+ elseif index == 4 then
+ if word:lower() == 'apis' then
+ apiQuery = true
+ else
+ request.err(400, "Invalid request")
+ end
+ end
+ index = index + 1
+ end
+ if id == nil then
getAllTenants()
else
- local id = uri:sub(10)
- getTenant(id)
+ if apiQuery == false then
+ getTenant(id)
+ else
+ getTenantAPIs(id)
+ end
end
end
@@ -319,23 +425,53 @@
end
--- Get tenant by its id
--- @param id
+-- @param id tenant id
function getTenant(id)
local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
local tenant = redis.getTenant(red, id)
+ if tenant == nil then
+ request.err(404, utils.concatStrings({"Unknown tenant id ", id }))
+ end
redis.close(red)
ngx.header.content_type = "application/json; charset=utf-8"
request.success(200, cjson.encode(tenant))
end
+--- Get APIs associated with tenant
+-- @param id tenant id
+function getTenantAPIs(id)
+ local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
+ local res = redis.getAllAPIs(red)
+ redis.close(red)
+ local apiList = {}
+ for k, v in pairs(res) do
+ if k%2 == 0 then
+ local decoded = cjson.decode(v)
+ if decoded.tenantId == id then
+ apiList[#apiList+1] = decoded
+ end
+ end
+ end
+ apiList = cjson.encode(apiList)
+ ngx.header.content_type = "application/json; charset=utf-8"
+ request.success(200, apiList)
+end
+
--- Delete tenant from gateway
-- DELETE http://0.0.0.0:9000/Tenants/<id>
function _M.deleteTenant()
local uri = string.gsub(ngx.var.request_uri, "?.*", "")
- if uri:len() <= 9 then
+ local index = 1
+ local id
+ for word in string.gmatch(uri, '([^/]+)') do
+ if index == 3 then
+ id = word
+ end
+ index = index + 1
+ end
+ if id == nil then
request.err(400, "No id specified.")
end
- local id = uri:sub(10)
local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
redis.deleteTenant(red, id)
redis.close(red)
@@ -366,6 +502,10 @@
request.success(200, "Unsubscribed to redis")
end
+---------------------------
+------ Subscriptions ------
+---------------------------
+
--- Add an apikey/subscription to redis
-- PUT http://0.0.0.0:9000/subscriptions
-- Body:
@@ -387,10 +527,6 @@
request.success(200, "Subscription created.")
end
----------------------------
------- Subscriptions ------
----------------------------
-
--- Delete apikey/subscription from redis
-- DELETE http://0.0.0.0:9000/subscriptions
-- Body: