mirror of
https://github.com/Doodle3D/doodle3d-firmware.git
synced 2024-12-22 11:03:48 +01:00
Move request handling to request object; update and combine todo items to TODO.md; improve test api.
This commit is contained in:
parent
4847b59378
commit
6caa7d244e
58
TODO.md
58
TODO.md
@ -1,10 +1,12 @@
|
||||
# TODO (from api_info.lua)
|
||||
- The endpoint function info in response objects is incorrect when the global function is called with a blank argument,
|
||||
to cleanly solve this, module/function resolution should be moved from main() to the request object
|
||||
FIX: handle mod/func resolution and calling in request:handle() which returns a response object
|
||||
then send it using response:send("json")
|
||||
- fix up init script handling as described here: http://wiki.openwrt.org/doc/devel/packages#packaging.a.service
|
||||
- versioning/updating
|
||||
# TODO (new functionality)
|
||||
- save requested mod+func in request, as well as resolved function/pretty print version/function pointer (or subset…); then fix endpoint function name (when called with blank argument) in response objects to show pretty print name
|
||||
- fix init script handling as described here: http://wiki.openwrt.org/doc/devel/packages#packaging.a.service
|
||||
- write a simple client script to autotest as much of the api as possible
|
||||
extend the client script to run arbitrary post/get requests
|
||||
- document REST API (mention rq IDs and endpoint information, list endpoints+args+CRUD type, unknown values are empty fields)
|
||||
(describe fail/error difference: fail is valid rq..could not comply, while error is invalid rq _or_ system error)
|
||||
- use a slightly more descriptive success/error definition (e.g. errortype=system/missing-arg/generic)
|
||||
- steps to take regarding versioning/updating
|
||||
* versioning scheme
|
||||
* create feed location (e.g. www.doodle3d.com/firmware/packages) (see here: http://wiki.openwrt.org/doc/packages#third.party.packages)
|
||||
* create opkg (already present in bin/ar71xx/packages as .ipk file)
|
||||
@ -13,42 +15,38 @@
|
||||
* determine how opkg decides what is 'upgradeable'
|
||||
* at this point manual updating should be possible, now find out how to implement in lua (execve? or write a minimalistic binding to libopkg?)
|
||||
* expose through info API and/or system API; also provide a way (future) to flash a new image
|
||||
- dynamic AP name based on partial MAC
|
||||
- dynamic AP name based on partial MAC (set once on installation and then only upon explicit request? (e.g. api/config/wifiname/default))
|
||||
- require api functions which change state to be invoked as post request
|
||||
- add functions to test network connectivity in steps (ifup? hasip? resolve? ping?) to network or test
|
||||
- add API functions to test network connectivity in steps (ifup? hasip? resolve? ping?) to network or test
|
||||
- add more config options to package, which should act as defaults for a config file on the system; candidates:
|
||||
reconf.WWW_RENAME_NAME, wifihelper.{AP_ADDRESS, AP_NETMASK, (NET)}
|
||||
<https://github.com/2ion/ini.lua>
|
||||
|
||||
|
||||
# Ideas / issues to work out
|
||||
- generally, for configuration keys, it could be a good idea to use the concept of default values so it's always possible to return to a 'sane default config'
|
||||
- add system api module? with check-updates/do-update/etc
|
||||
- write a simple client script to autotest as much of the api as possible
|
||||
- extend the client script to run arbitrary post/get requests
|
||||
|
||||
|
||||
# TODO (from main.lua)
|
||||
- document REST API (mention rq IDs and endpoint information, list endpoints+args+CRUD type, unknown values are empty fields)
|
||||
(describe fail/error difference: fail is valid rq..could not comply, while error is invalid rq _or_ system error)
|
||||
- use a slightly more descriptive success/error definition (e.g. errortype=system/missing-arg/generic)
|
||||
- how to handle requests which need a restart of uhttpd? (e.g. network/openap)
|
||||
- a plain GET request (no ajax/script) runs the risk of timing out on lengthy operations: implement polling in API to get progress updates?
|
||||
(this would require those operations to run in a separate daemon process which can be monitored by the CGI handler)
|
||||
(!!!is this true? it could very well be caused by a uhttpd restart)
|
||||
- protect dump function against reference loops (see: http://lua-users.org/wiki/TableSerialization, json also handles this well)
|
||||
- licensing (also for hardware and firmware) + credits for external code and used ideas
|
||||
<http://www.codinghorror.com/blog/2007/04/pick-a-license-any-license.html>
|
||||
- (this is an old todo item from network:available(), might still be relevant at some point)
|
||||
extend netconf interface to support function arguments (as tables) so wifihelper functionality can be integrated
|
||||
but how? idea: pass x_args={arg1="a",arg2="2342"} for component 'x'
|
||||
or: allow alternative for x="y" --> x={action="y", arg1="a", arg2="2342"}
|
||||
in any case, arguments should be put in a new table to pass to the function (since order is undefined it must be an assoc array)
|
||||
- perhaps opkg+safeboot could be useful in the update mechanism?
|
||||
- add config option to compile sources using luac _or_ add an option to minify the lua code
|
||||
|
||||
|
||||
# TODO
|
||||
- in captive portal mode, https is not redirected
|
||||
- see if updating works when a new version is 'released' (probably needs a real feed)
|
||||
- perhaps opkg+safeboot could be useful in the update mechanism?
|
||||
- licensing (also for hardware and firmware) + credits for external code and used ideas
|
||||
http://www.codinghorror.com/blog/2007/04/pick-a-license-any-license.html
|
||||
- add more config options to package, which should act as defaults for a config file on the system; candidates:
|
||||
reconf.WWW_RENAME_NAME, wifihelper.{AP_ADDRESS, AP_NETMASK, (NET)}
|
||||
https://github.com/2ion/ini.lua
|
||||
- add config option to compile sources using luac _or_ add an option to minify the lua code
|
||||
- add dependency on either wr703n or mr3020 (and probably comment it out again to avoid being to conservative...)
|
||||
- relocatabilty of package (take root prefix into consideration everywhere)
|
||||
# Bugs
|
||||
- in captive portal mode, https is not redirected
|
||||
- using iwinfo with interface name 'radio0' yields very little 'info' output while wlan0 works fine.
|
||||
However, sometimes wlan0 disappears (happened after trying to associate with non-existing network)...why?
|
||||
- protect dump function against reference loops (see <http://lua-users.org/wiki/TableSerialization>, json also handles this well)
|
||||
- relocatabilty of package (take root prefix into consideration everywhere)
|
||||
|
||||
|
||||
# Logos
|
||||
|
86
src/main.lua
86
src/main.lua
@ -1,24 +1,3 @@
|
||||
--[[
|
||||
TODO:
|
||||
- document REST API (mention rq IDs and endpoint information, list endpoints+args+CRUD type, unknown values are empty fields)
|
||||
(describe fail/error difference: fail is valid rq..could not comply, while error is invalid rq _or_ system error)
|
||||
- use a slightly more descriptive success/error definition (e.g. errortype=system/missing-arg/generic)
|
||||
- how to handle requests which need a restart of uhttpd? (e.g. network/openap)
|
||||
- a plain GET request (no ajax/script) runs the risk of timing out on lengthy operations: implement polling in API to get progress updates?
|
||||
(this would require those operations to run in a separate daemon process which can be monitored by the CGI handler)
|
||||
(!!!is this true? it could very well be caused by a uhttpd restart)
|
||||
- protect dump function against reference loops (see: http://lua-users.org/wiki/TableSerialization, json also handles this well)
|
||||
- (this is an old todo item from network:available(), might still be relevant at some point)
|
||||
extend netconf interface to support function arguments (as tables) so wifihelper functionality can be integrated
|
||||
but how? idea: pass x_args={arg1="a",arg2="2342"} for component 'x'
|
||||
or: allow alternative for x="y" --> x={action="y", arg1="a", arg2="2342"}
|
||||
in any case, arguments should be put in a new table to pass to the function (since order is undefined it must be an assoc array)
|
||||
|
||||
NOTES:
|
||||
- using iwinfo with interface name 'radio0' yields very little 'info' output while wlan0 works fine.
|
||||
However, sometimes wlan0 disappears (happened after trying to associate with non-existing network)...why?
|
||||
]]--
|
||||
|
||||
local l = require("logger")
|
||||
local RequestClass = require("rest.request")
|
||||
local ResponseClass = require("rest.response")
|
||||
@ -61,38 +40,6 @@ local function init()
|
||||
return true
|
||||
end
|
||||
|
||||
--usually returns function+nil, function+number in case of number in place of function name; or
|
||||
--nil+string if given arguments could not be resolved
|
||||
local function resolveApiFunction(mod, func)
|
||||
if mod == nil then return nil, ("missing module name in CGI call") end
|
||||
|
||||
local ok, mObj
|
||||
local reqModPath = "rest.api.api_" .. mod
|
||||
|
||||
if DEBUG_PCALLS then ok, mObj = true, require(reqModPath)
|
||||
else ok, mObj = pcall(require, reqModPath)
|
||||
end
|
||||
|
||||
if ok == false then return nil, ("API module '" .. mod .. "' does not exist") end
|
||||
|
||||
if mObj == nil then return nil, ("API module '" .. mod .. "' could not be found") end
|
||||
|
||||
if mObj.isApi ~= true then return nil, ("module '" .. mod .. "' is not part of the CGI API") end
|
||||
|
||||
if (func == nil or func == '') then func = "_global" end --treat empty function name as nil
|
||||
local f = mObj[func]
|
||||
|
||||
if (type(f) ~= "function") then
|
||||
if tonumber(func) ~= nil then
|
||||
return mObj["_global"], tonumber(func)
|
||||
else
|
||||
return nil, ("function '" .. func .. "' does not exist in API module '" .. mod .. "'")
|
||||
end
|
||||
end
|
||||
|
||||
return f
|
||||
end
|
||||
|
||||
local function main()
|
||||
local rq = RequestClass.new(postData, DEBUG_PCALLS) -- initializes itself using various environment variables and the arg array
|
||||
|
||||
@ -111,35 +58,10 @@ end
|
||||
|
||||
else
|
||||
io.write ("Content-type: text/plain\r\n\r\n")
|
||||
local response, err = rq:handle()
|
||||
|
||||
local mod = rq:getApiModule()
|
||||
local func = rq:getApiFunction()
|
||||
|
||||
local sf,sr = resolveApiFunction(mod, func)
|
||||
if (sf ~= nil) then
|
||||
if (sr ~= nil) then
|
||||
rq:setBlankArgument(sr)
|
||||
end
|
||||
|
||||
local ok, r
|
||||
if DEBUG_PCALLS then ok, r = true, sf(rq)
|
||||
else ok, r = pcall(sf, rq)
|
||||
end
|
||||
|
||||
if ok == true then
|
||||
print(r:serializeAsJson())
|
||||
else
|
||||
local resp = ResponseClass.new(rq)
|
||||
resp:setError("call to function '" .. mod .. "/" .. sr .. "' failed")
|
||||
print(resp:serializeAsJson())
|
||||
l:error("calling function '" .. func .. "' in API module '" .. mod .. "' somehow failed ('" .. r .. "')")
|
||||
end
|
||||
else
|
||||
local resp = ResponseClass.new(rq)
|
||||
resp:setError("function unknown '" .. (mod or "<empty>") .. "/" .. (func or "<global>") .. "'")
|
||||
print(resp:serializeAsJson())
|
||||
l:error("could not resolve requested API function ('" .. sr .. "')")
|
||||
end
|
||||
if err ~= nil then l:error(err) end
|
||||
response:send()
|
||||
end
|
||||
end
|
||||
|
||||
@ -147,7 +69,7 @@ local s, msg = init()
|
||||
if s == false then
|
||||
local resp = ResponseClass.new()
|
||||
resp:setError("initialization failed (" .. msg .. ")")
|
||||
print(resp:serializeAsJson()) --FIXME: this message does not seem to be sent
|
||||
resp:send() --FIXME: this message does not seem to be sent
|
||||
l:error("initialization failed (" .. msg .. ")") --NOTE: this assumes the logger has been inited properly, despite init() having failed
|
||||
os.exit(1)
|
||||
else
|
||||
|
@ -17,19 +17,22 @@ end
|
||||
|
||||
function M.success(d)
|
||||
local r = ResponseClass.new(d)
|
||||
r:setSuccess()
|
||||
r:setSuccess("this successful response has been generated on purpose")
|
||||
r:addData("url", "http://xkcd.com/349/")
|
||||
return r
|
||||
end
|
||||
|
||||
function M.fail(d)
|
||||
local r = ResponseClass.new(d)
|
||||
r:setFail()
|
||||
r:setFail("this failure has been generated on purpose")
|
||||
r:addData("url", "http://xkcd.com/336/")
|
||||
return r
|
||||
end
|
||||
|
||||
function M.error(d)
|
||||
local r = ResponseClass.new(d)
|
||||
r:setError("this error has been generated on purpose")
|
||||
r:addData("url", "http://xkcd.com/1024/")
|
||||
return r
|
||||
end
|
||||
|
||||
|
@ -1,8 +1,11 @@
|
||||
local urlcode = require("util.urlcode")
|
||||
local ResponseClass = require("rest.response")
|
||||
|
||||
local M = {}
|
||||
M.__index = M
|
||||
|
||||
local GLOBAL_API_FUNCTION_NAME = "_global"
|
||||
|
||||
local function kvTableFromUrlEncodedString(encodedText)
|
||||
local args = {}
|
||||
if (encodedText ~= nil) then
|
||||
@ -113,4 +116,75 @@ function M:getAll()
|
||||
end
|
||||
end
|
||||
|
||||
--returns either a module object, or nil+errmsg
|
||||
local function resolveApiModule(modname)
|
||||
if modname == nil then return nil, "missing module name" end
|
||||
if string.find(modname, "_") == 1 then return nil, "module names starting with '_' are preserved for internal use" end
|
||||
|
||||
local reqModName = "rest.api.api_" .. modname
|
||||
local ok, modObj
|
||||
|
||||
--TODO: create config.lua which contains DEBUG_PCALLS (nothing else for the moment)
|
||||
if DEBUG_PCALLS then ok, modObj = true, require(reqModName)
|
||||
else ok, modObj = pcall(require, reqModName)
|
||||
end
|
||||
|
||||
if ok == false then return nil, "API module does not exist" end
|
||||
if modObj == nil then return nil, "API module could not be found" end
|
||||
if modObj.isApi ~= true then return nil, "module is not part of the CGI API" end
|
||||
|
||||
return modObj
|
||||
end
|
||||
|
||||
--returns funcobj+nil (usual), funcobj+number (global func with blank arg), or nil+errmsg (unresolvable or inaccessible)
|
||||
local function resolveApiFunction(modname, funcname)
|
||||
if funcname and string.find(funcname, "_") == 1 then return nil, "function names starting with '_' are preserved for internal use" end
|
||||
|
||||
local mod, msg = resolveApiModule(modname)
|
||||
|
||||
if (funcname == nil or funcname == '') then funcname = GLOBAL_API_FUNCTION_NAME end --treat empty function name as nil
|
||||
local f = mod[funcname]
|
||||
local funcNumber = tonumber(funcname)
|
||||
|
||||
if (type(f) == "function") then
|
||||
return f
|
||||
elseif funcNumber ~= nil then
|
||||
return mod[GLOBAL_API_FUNCTION_NAME], funcNumber
|
||||
else
|
||||
return nil, ("function '" .. funcname .. "' does not exist in API module '" .. modname .. "'")
|
||||
end
|
||||
end
|
||||
|
||||
--returns either a response object+nil, or response object+errmsg
|
||||
function M:handle()
|
||||
|
||||
--TEMP: should be moved to init
|
||||
local mod = self:getApiModule()
|
||||
local func = self:getApiFunction()
|
||||
local sf, sr = resolveApiFunction(mod, func)
|
||||
|
||||
local resp = ResponseClass.new(rq) --TEMP: do not do this before resolving. after resolving has been moved to init that will be automatically true
|
||||
|
||||
if (sf ~= nil) then
|
||||
if (sr ~= nil) then self:setBlankArgument(sr) end
|
||||
|
||||
local ok, r
|
||||
if DEBUG_PCALLS then ok, r = true, sf(self)
|
||||
else ok, r = pcall(sf, self)
|
||||
end
|
||||
|
||||
if ok == true then
|
||||
return r, nil
|
||||
else
|
||||
resp:setError("call to function '" .. mod .. "/" .. sr .. "' failed")
|
||||
return resp, ("calling function '" .. func .. "' in API module '" .. mod .. "' somehow failed ('" .. r .. "')")
|
||||
end
|
||||
else
|
||||
resp:setError("function unknown '" .. (mod or "<empty>") .. "/" .. (func or "<global>") .. "'")
|
||||
return resp, ("could not resolve requested API function ('" .. sr .. "')")
|
||||
end
|
||||
|
||||
return resp
|
||||
end
|
||||
|
||||
return M
|
||||
|
@ -61,4 +61,8 @@ function M:serializeAsJson()
|
||||
return JSON:encode(self.body)
|
||||
end
|
||||
|
||||
function M:send()
|
||||
print(self:serializeAsJson())
|
||||
end
|
||||
|
||||
return M
|
||||
|
Loading…
Reference in New Issue
Block a user