-
Notifications
You must be signed in to change notification settings - Fork 0
/
builder.lua
294 lines (267 loc) · 7.63 KB
/
builder.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
------------------------------------------------------------------------
--- Purpose: Build api http routes with params using the annotations ---
------------------------------------------------------------------------
local EXPORT_CODE = "-- @API"
local _routes = {} -- holds all routes available
local _funcMap = {} -- create a mapping for perfs (no need to iterate over _routes for each request)
function getFuncMapping()
return _funcMap
end
function buildAll()
buildRoutes()
buildFuncMapping()
end
addEventHandler("onResourceStart", resourceRoot, buildAll)
-- ex: _funcMap["POST"]["players"]["kick"] = "kick"
function buildFuncMapping()
_funcMap = {}
for k, r in ipairs(_routes) do
-- method
if not _funcMap[r.m] then
_funcMap[r.m] = {}
end
-- category
if not _funcMap[r.m][r.c] then
_funcMap[r.m][r.c] = {}
end
-- func
if not _funcMap[r.m][r.c][r.f or "default"] then
_funcMap[r.m][r.c][r.f or "default"] = r.fn
end
end
-- var_dump("-v", _funcMap)
end
function buildRoutes()
-- outputConsole("-- buildRoutes --")
local routes = getApiRoutesFromScripts()
for k, route in ipairs(routes) do
addRoute(route.path, route.method, route.funcName)
end
updateApiExports()
-- var_dump("-v", _routes)
end
function getApiRoutesFromScripts()
local apiRoutes = {path, method, funcName}
local sScripts = getServerScripts()
if #sScripts <= 0 then return apiRoutes end
-- var_dump("-v", sScripts)
for k, script in ipairs(sScripts) do
local file = fileOpen(script, true)
if file then
local buffer = ""
while not fileIsEOF(file) do
buffer = buffer..fileRead(file, 500)
end
local lines = split(buffer, "\n")
local tmp = nil
for k, line in ipairs(lines) do
if tmp == nil then
local trimmed = string.trim(line)
local exportCode = string.trim(EXPORT_CODE)
local ecStart, ecEnd = string.find(trimmed, exportCode)
if ecStart and ecEnd then
local all, method, path = trimmed:match("\(\"(.*)\",\"(.*)\"\)")
if method and path then
tmp = {method=method, path=path}
end
end
else
local funcName = extractFuncName(line)
table.insert(apiRoutes, {path = tmp.path, method = tmp.method, funcName = funcName})
tmp = nil
end
end
fileClose(file)
end
end
return apiRoutes
end
function extractFuncName(str)
return str:match("function (%w+)%s*\(.*\)")
end
function getServerScripts()
local sScripts = {}
local rootNode = xmlLoadFile("meta.xml")
if not rootNode then return sScripts end
local nodes = xmlNodeGetChildren(rootNode)
if nodes then
for k, node in ipairs(nodes) do
if xmlNodeGetName(node) == "script" then
local fileName = xmlNodeGetAttribute(node, "src")
local stype = xmlNodeGetAttribute(node, "type") or "server"
if fileName and stype == "server" then
table.insert(sScripts, fileName)
end
end
end
end
xmlUnloadFile(rootNode)
return sScripts
end
-- ex: "/players/{id}"
-- ex: "/players/kick"
function addRoute( route, method, funcName )
local parts = split(route, "/")
local category = nil
local func = nil
if #parts >= 1 then
category = parts[1]
if #parts >= 2 then
local isParam = string.match(parts[2], "^{%a*}$")
if not isParam then
func = parts[2]
end
end
end
table.insert(_routes, {m=method, c=category, f=func, fn=funcName})
-- outputConsole("Route: m:"..tostring(method).." c:"..tostring(category).." f:"..tostring(func).. " fn:"..tostring(funcName))
end
function updateApiExports()
local rootNode = xmlLoadFile("meta.xml")
if rootNode then
-- Cleaning
local nodes = xmlNodeGetChildren(rootNode)
if nodes then
for k, node in ipairs(nodes) do
if xmlNodeGetName(node) == "export" then
if xmlNodeGetAttribute(node, "api") == "true" then
xmlDestroyNode(node)
end
end
end
end
-- Adding
for k, r in ipairs(_routes) do
local node = xmlCreateChild(rootNode, "export")
xmlNodeSetAttribute(node, "api", "true")
xmlNodeSetAttribute(node, "function", r.fn)
xmlNodeSetAttribute(node, "http", "true")
end
return xmlSaveFile(rootNode) and xmlUnloadFile(rootNode)
end
end
--------------------------------------------------------
function string.trim(str)
return string.gsub(str, "%s+", "")
end
function var_dump(...)
-- default options
local verbose = false
local firstLevel = true
local outputDirectly = true
local noNames = false
local indentation = "\t\t\t\t\t\t"
local depth = nil
local name = nil
local output = {}
for k,v in ipairs(arg) do
-- check for modifiers
if type(v) == "string" and k < #arg and v:sub(1,1) == "-" then
local modifiers = v:sub(2)
if modifiers:find("v") ~= nil then
verbose = true
end
if modifiers:find("s") ~= nil then
outputDirectly = false
end
if modifiers:find("n") ~= nil then
verbose = false
end
if modifiers:find("u") ~= nil then
noNames = true
end
local s,e = modifiers:find("d%d+")
if s ~= nil then
depth = tonumber(string.sub(modifiers,s+1,e))
end
-- set name if appropriate
elseif type(v) == "string" and k < #arg and name == nil and not noNames then
name = v
else
if name ~= nil then
name = ""..name..": "
else
name = ""
end
local o = ""
if type(v) == "string" then
table.insert(output,name..type(v).."("..v:len()..") \""..v.."\"")
elseif type(v) == "userdata" then
local elementType = "no valid MTA element"
if isElement(v) then
elementType = getElementType(v)
end
table.insert(output,name..type(v).."("..elementType..") \""..tostring(v).."\"")
elseif type(v) == "table" then
local count = 0
for key,value in pairs(v) do
count = count + 1
end
table.insert(output,name..type(v).."("..count..") \""..tostring(v).."\"")
if verbose and count > 0 and (depth == nil or depth > 0) then
table.insert(output,"\t{")
for key,value in pairs(v) do
-- calls itself, so be careful when you change anything
local newModifiers = "-s"
if depth == nil then
newModifiers = "-sv"
elseif depth > 1 then
local newDepth = depth - 1
newModifiers = "-svd"..newDepth
end
local keyString, keyTable = var_dump(newModifiers,key)
local valueString, valueTable = var_dump(newModifiers,value)
if #keyTable == 1 and #valueTable == 1 then
table.insert(output,indentation.."["..keyString.."]\t=>\t"..valueString)
elseif #keyTable == 1 then
table.insert(output,indentation.."["..keyString.."]\t=>")
for k,v in ipairs(valueTable) do
table.insert(output,indentation..v)
end
elseif #valueTable == 1 then
for k,v in ipairs(keyTable) do
if k == 1 then
table.insert(output,indentation.."["..v)
elseif k == #keyTable then
table.insert(output,indentation..v.."]")
else
table.insert(output,indentation..v)
end
end
table.insert(output,indentation.."\t=>\t"..valueString)
else
for k,v in ipairs(keyTable) do
if k == 1 then
table.insert(output,indentation.."["..v)
elseif k == #keyTable then
table.insert(output,indentation..v.."]")
else
table.insert(output,indentation..v)
end
end
for k,v in ipairs(valueTable) do
if k == 1 then
table.insert(output,indentation.." => "..v)
else
table.insert(output,indentation..v)
end
end
end
end
table.insert(output,"\t}")
end
else
table.insert(output,name..type(v).." \""..tostring(v).."\"")
end
name = nil
end
end
local string = ""
for k,v in ipairs(output) do
if outputDirectly then
outputConsole(v)
end
string = string..v
end
return string, output
end