local replace_ok = require('route_mapper_compat').native
local log = require('mc.logging')
local http = require('http')
local cjson = require('route_mapper_cjson')
local Controller = require('http.controller')
local webrest_utils = require('webrest_utils')
local routes_root = require('web_backend.routes.root')
local task_mgnt = require('web_backend.lualib.task_mgnt')
local reply_bad_request = http.reply_bad_request
local vos, mapper_intf
if not replace_ok then
vos = require('utils.vos')
mapper_intf = require('route_mapper.interface')
end
local load_controller = {}
local function merge_rsp_extra_header(rsp)
if not rsp or not rsp.header or not rsp.extra_header then
return
end
for k, v in pairs(rsp.extra_header) do
rsp.header[k] = v
end
end
local function replace_get(_self, ctx)
local req = {
user = ctx.user,
params = ctx.req.params,
path = ctx.req.path,
header = ctx.req.header,
mapper_session = ctx.mapper_session,
}
local query = require('http.query')
req.query = query.parse_query(ctx.req.query)
local rsp_body = {}
local rsp_header = {}
local rsp = {
body = rsp_body,
header = rsp_header,
}
return _self:process_get(req, rsp)
end
local function get(_self, ctx)
local req = { user = ctx.user, params = ctx.req.params, path = ctx.req.path, header = ctx.req.header }
local query = require('http.query')
req.query = query.parse_query(ctx.req.query)
local rsp_body = {}
local rsp_header = {}
local rsp = { body = rsp_body, header = rsp_header }
return _self:process_get(req, rsp)
end
local function replace_on_get(_self, req, rsp)
if not req.user then
req.user = {
Interface = 'WEB',
}
else
req.user.Interface = 'WEB'
end
local result = _self.route_mapper:execute(req.path, 'get', {
is_auth = true,
header = req.header,
body = req.body,
query = req.query,
user = req.user,
location_id = webrest_utils.get_location_id(),
session = req.mapper_session,
})
rsp.body = result.body
rsp.extra_header = result.headers
rsp.status = result.status
if #result.errors ~= 0 then
return webrest_utils.parse_route_mapper_error(result.errors)
end
end
local function on_get(_self, req, rsp)
local err, extra_info
if not req.user then
req.user = { Interface = 'WEB' }
else
req.user['Interface'] = 'WEB'
end
err, rsp.body, extra_info = _self.worker_ctrl:process(req.path, 'get', {
header = req.header,
body = req.body,
query = req.query,
user = req.user,
is_auth = true,
location_id = webrest_utils.get_location_id(),
})
rsp.extra_header = extra_info and extra_info.RspHeader
if err and #err ~= 0 then
return webrest_utils.parse_route_mapper_error(err)
end
end
local function on_get_rsp_header(_self, req, rsp)
merge_rsp_extra_header(rsp)
end
function load_controller.get(controller)
if not controller then
return
end
if replace_ok then
controller.get = replace_get
controller.on_get = replace_on_get
else
controller.get = get
controller.on_get = on_get
end
controller.on_get_rsp_header = on_get_rsp_header
end
local function patch(_self, ctx)
local reply = _self:auth(ctx)
if not reply:isOk() then
return reply
end
local is_ok, body = pcall(cjson.json_object_ordered_decode, ctx.req.body)
if not is_ok or not cjson.json_object_is_object(body) then
return http.reply_bad_request('UnrecognizedRequestBody')
end
local req = {
user = ctx.user,
params = ctx.req.params,
path = ctx.req.path,
header = ctx.req.header,
body = body,
result = http.reply_ok(),
}
if replace_ok then
req.mapper_session = ctx.mapper_session
end
local query = require('http.query')
req.query = query.parse_query(ctx.req.query)
local rsp_body = {}
local rsp_header = {}
local rsp = { body = rsp_body, header = rsp_header }
return _self:process_patch(req, rsp)
end
local function replace_on_patch(_self, req, rsp)
local result = _self.route_mapper:execute(req.path, 'patch', {
header = req.header,
body = req.body,
query = req.query,
user = req.user,
session = req.mapper_session,
})
rsp.extra_header = result.headers
rsp.status = result.status
local get_result = _self.route_mapper:execute(req.path, 'get', {
header = req.header,
body = req.body,
query = req.query,
user = req.user,
session = req.mapper_session,
})
rsp.body = get_result.body
if #result.errors ~= 0 then
local reply = webrest_utils.parse_route_mapper_error(result.errors)
return reply
end
end
local function on_patch(_self, req, rsp)
local err, _, extra_info = _self.worker_ctrl:process(
req.path,
'patch',
{ header = req.header, body = req.body, query = req.query, user = req.user }
)
rsp.extra_header = extra_info and extra_info.RspHeader
_, rsp.body = _self.worker_ctrl:process(
req.path,
'get',
{ header = req.header, body = req.body, query = req.query, user = req.user }
)
if err and #err ~= 0 then
local result = webrest_utils.parse_route_mapper_error(err)
return result
end
end
local function on_patch_rsp_header(_self, req, rsp)
merge_rsp_extra_header(rsp)
end
function load_controller.patch(controller)
if not controller then
return
end
if replace_ok then
controller.on_patch = replace_on_patch
else
controller.on_patch = on_patch
end
controller.patch = patch
controller.on_patch_rsp_header = on_patch_rsp_header
end
local function replace_post(_self, ctx)
local is_ok, body = pcall(cjson.json_object_ordered_decode, ctx.req.body)
if not is_ok or not (cjson.json_object_is_object(body)) then
return http.reply_bad_request('UnrecognizedRequestBody')
end
local req = {
user = ctx.user,
params = ctx.req.params,
path = ctx.req.path,
header = ctx.req.header,
body = body,
mapper_session = ctx.mapper_session,
}
local query = require('http.query')
req.query = query.parse_query(ctx.req.query)
local rsp_body = {}
local rsp_header = {}
local rsp = {
body = rsp_body,
header = rsp_header,
}
return _self:process_post(req, rsp)
end
local function post(_self, ctx)
local unrecognized_req = false
local is_ok, body = pcall(cjson.json_object_ordered_decode, ctx.req.body)
if not is_ok or not cjson.json_object_is_object(body) then
unrecognized_req = true
end
local req = {
user = ctx.user,
params = ctx.req.params,
path = ctx.req.path,
header = ctx.req.header,
body = body,
unrecognized_req = unrecognized_req,
}
local query = require('http.query')
req.query = query.parse_query(ctx.req.query)
local rsp_body = {}
local rsp_header = {}
local rsp = { body = rsp_body, header = rsp_header }
return _self:process_post(req, rsp)
end
local function replace_on_post(_self, req, rsp)
if not req.user then
req.user = {
Interface = 'WEB',
}
else
req.user.Interface = 'WEB'
end
local result = _self.route_mapper:execute(req.path, 'post', {
is_auth = true,
header = req.header,
body = req.body,
query = req.query,
user = req.user,
location_id = webrest_utils.get_location_id(),
session = req.mapper_session,
})
rsp.body = result.body
rsp.extra_header = result.headers
rsp.status = result.status
if #result.errors ~= 0 then
return webrest_utils.parse_route_mapper_error(result.errors)
end
local task_info = result.effects and result.effects.Tasks and result.effects.Tasks[1]
if task_info and tonumber(task_info.TaskId) and tonumber(task_info.TaskId) > 0 then
rsp.task = true
local task_id = task_mgnt.get_instance():create_new_task(
_self.app.bus,
task_info.Path,
task_info.TaskId,
task_info.Context,
task_info.PostTaskProcess
)
if not task_id then
return reply_bad_request('TaskLimitExceeded')
end
local taskinfo = task_mgnt.get_instance():get_task_info(task_id)
if not taskinfo then
return http.reply_not_found('ResourceMissingAtURI')
end
local body = {
url = '/UI/Rest/Task/' .. task_id,
}
return http.reply(http.HTTP_OK, body)
end
end
local function on_post(_self, req, rsp)
local err, extra_info
if not req.user then
req.user = { Interface = 'WEB' }
else
req.user['Interface'] = 'WEB'
end
err, rsp.body, extra_info = _self.worker_ctrl:process(req.path, 'post', {
header = req.header,
body = req.body,
query = req.query,
user = req.user,
is_auth = true,
unrecognized_req = req.unrecognized_req,
location_id = webrest_utils.get_location_id(),
})
rsp.extra_header = extra_info and extra_info.RspHeader
if err and #err ~= 0 then
return webrest_utils.parse_route_mapper_error(err)
end
if extra_info and extra_info.TaskInfo and extra_info.TaskInfo.TaskId > 0 then
rsp.task = true
local task_id = task_mgnt.get_instance():create_new_task(
_self.app.bus,
extra_info.TaskInfo.Path,
extra_info.TaskInfo.TaskId,
extra_info.TaskInfo.Context,
extra_info.TaskInfo.PostTaskProcess
)
if not task_id then
return reply_bad_request('TaskLimitExceeded')
end
local taskinfo = task_mgnt.get_instance():get_task_info(task_id)
if not taskinfo then
return http.reply_not_found('ResourceMissingAtURI')
end
local body = { url = '/UI/Rest/Task/' .. task_id }
return http.reply(http.HTTP_OK, body)
end
end
local function on_post_rsp_header(_self, req, rsp)
merge_rsp_extra_header(rsp)
end
function load_controller.post(controller)
if not controller then
return
end
if replace_ok then
controller.post = replace_post
controller.on_post = replace_on_post
else
controller.post = post
controller.on_post = on_post
end
controller.on_post_rsp_header = on_post_rsp_header
end
local function delete(_self, ctx)
local reply = _self:auth(ctx)
if not reply:isOk() then
return reply
end
local is_ok, body = pcall(function()
local req_body = (ctx.req.body == nil or ctx.req.body == '') and '{}' or ctx.req.body
return cjson.json_object_ordered_decode(req_body)
end)
if not is_ok or not cjson.json_object_is_object(body) then
return http.reply_bad_request('UnrecognizedRequestBody')
end
local req = {
user = ctx.user,
params = ctx.req.params,
path = ctx.req.path,
header = ctx.req.header,
body = body,
}
if replace_ok then
req.mapper_session = ctx.mapper_session
end
local rsp_body = {}
local rsp_header = {}
local rsp = { body = rsp_body, header = rsp_header }
return _self:process_delete(req, rsp)
end
local function replace_on_delete(_self, req, rsp)
local result = _self.route_mapper:execute(req.path, 'delete', {
header = req.header,
body = req.body,
query = req.query,
user = req.user,
session = req.mapper_session,
})
rsp.body = result.body
rsp.extra_header = result.headers
rsp.status = result.status
if #result.errors ~= 0 then
return webrest_utils.parse_route_mapper_error(result.errors)
end
end
local function on_delete(_self, req, rsp)
local err, extra_info
err, rsp.body, extra_info = _self.worker_ctrl:process(
req.path,
'delete',
{ header = req.header, body = req.body, query = req.query, user = req.user }
)
rsp.extra_header = extra_info and extra_info.RspHeader
if err and #err ~= 0 then
return webrest_utils.parse_route_mapper_error(err)
end
end
local function on_delete_rsp_header(_self, req, rsp)
merge_rsp_extra_header(rsp)
end
function load_controller.delete(controller)
if not controller then
return
end
if replace_ok then
controller.on_delete = replace_on_delete
else
controller.on_delete = on_delete
end
controller.delete = delete
controller.on_delete_rsp_header = on_delete_rsp_header
end
local function load_mapper_controller(server)
if not (server and server.mapper) then
error('in-process route mapper must be initialized before mapped controllers', 0)
end
local controller = Controller.new('/')
controller.parent = routes_root.Route
for _, method in ipairs({ 'get', 'post', 'patch', 'delete' }) do
load_controller[method](controller)
end
controller.app = server.app
controller.route_mapper = server.route_mapper
controller.server = server
server.mapper_controller = controller
end
local function load_json_controllers(server, resources)
if not cjson.json_object_is_array(resources) then
log:error('Resources type error(not array), load failed.')
return
end
local interfaces, method_type
for _, v in pairs(resources) do
if not v or not v.Uri then
goto continue_resources
end
local controller = Controller.new(v.Uri)
controller.parent = routes_root.Route
interfaces = v.Interfaces
if not interfaces then
log:error('The interfaces is incorrect(Uri=%s)', v.Uri)
goto continue_resources
end
for _, value in ipairs(interfaces) do
if not value['Type'] then
log:error('The Type of Interfaces is empty(Uri=%s)', v.Uri)
goto continue_interfaces
end
method_type = string.lower(value['Type'])
local handle = load_controller[method_type]
if handle then
handle(controller)
end
::continue_interfaces::
end
server:add_controller(controller)
::continue_resources::
end
end
if replace_ok then
load_controller.load_mapper_controller = load_mapper_controller
else
load_controller.load_json_controllers = load_json_controllers
end
if replace_ok then
function load_controller.dispatch_mapper_controller(server, ctx, session)
local path = ctx.req.path or ctx.req.url
local method = string.lower(ctx.req.method)
if not (server.mapper:match(path, method, session)) then
return false
end
local controller = assert(server.mapper_controller, 'mapped request controller is not initialized')
ctx.mapper_session = session
ctx.req.params = ctx.req.params or {}
local handle = controller:get_handle(method)
if not handle then
return false
end
return true, handle(controller, ctx)
end
else
local function load_one_controller(server, mapper)
if type(mapper) ~= 'table' then
return
end
for uri, method_types in pairs(mapper) do
if type(method_types) ~= 'table' then
log:error('The resource is incorrect.')
goto continue_resources
end
local controller = Controller.new(uri)
controller.parent = routes_root.Route
for method_type, flag in pairs(method_types) do
if not flag then
log:error('The Type of Interfaces is empty.')
goto continue_interfaces
end
local handle = load_controller[method_type]
if handle then
handle(controller)
end
::continue_interfaces::
end
server:add_controller(controller)
::continue_resources::
end
end
function load_controller.load_controllers(server, mapper)
for key, value in pairs(mapper) do
if string.match(key, '%.json$') ~= nil then
load_one_controller(server, value)
elseif type(value) == 'table' then
load_controller.load_controllers(server, value)
end
end
end
end
local function load_static_controller(server)
if not server then
log:error('Input param server is empty.')
return
end
server:add_controller(require('firmware_inventory'))
server:add_controller(require('download_static_file'))
server:add_controller(require('task'))
server:add_controller(require('auth_jar'))
server:add_controller(require('ntp_importkeys'))
end
function load_controller.load_route_controller(server, conf_path)
if not server or not conf_path then
log:error('Input param server or conf_path is empty.')
return
end
load_static_controller(server)
if replace_ok then
load_controller.load_mapper_controller(server)
return
end
if not vos.get_file_accessible(conf_path .. '/config.lua') then
mapper_intf.load_mapping(server.app.bus, conf_path, function(obj)
load_json_controllers(server, obj.Resources)
end)
else
local mapper = mapper_intf.load_intf_mapping(server.app.bus, conf_path)
load_controller.load_controllers(server, mapper)
end
end
return load_controller