- Implements Apache APISIX packaging for Cloudron platform. - Includes Dockerfile, CloudronManifest.json, and start.sh. - Configured to use Cloudron's etcd addon. 🤖 Generated with Gemini CLI Co-Authored-By: Gemini <noreply@google.com>
244 lines
6.2 KiB
Lua
244 lines
6.2 KiB
Lua
--
|
|
-- Licensed to the Apache Software Foundation (ASF) under one or more
|
|
-- contributor license agreements. See the NOTICE file distributed with
|
|
-- this work for additional information regarding copyright ownership.
|
|
-- The ASF licenses this file to You under the Apache License, Version 2.0
|
|
-- (the "License"); you may not use this file except in compliance with
|
|
-- the License. You may obtain a copy of the License at
|
|
--
|
|
-- http://www.apache.org/licenses/LICENSE-2.0
|
|
--
|
|
-- Unless required by applicable law or agreed to in writing, software
|
|
-- distributed under the License is distributed on an "AS IS" BASIS,
|
|
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
-- See the License for the specific language governing permissions and
|
|
-- limitations under the License.
|
|
--
|
|
local core = require("apisix.core")
|
|
local xml2lua = require("xml2lua")
|
|
|
|
local json = core.json
|
|
local math = math
|
|
local ngx = ngx
|
|
local ngx_re = ngx.re
|
|
local pairs = pairs
|
|
local string = string
|
|
local table = table
|
|
local type = type
|
|
|
|
local support_content_type = {
|
|
["application/xml"] = true,
|
|
["application/json"] = true,
|
|
["text/plain"] = true,
|
|
["text/html"] = true,
|
|
["text/xml"] = true
|
|
}
|
|
|
|
local schema = {
|
|
type = "object",
|
|
properties = {
|
|
-- specify response delay time,default 0ms
|
|
delay = { type = "integer", default = 0 },
|
|
-- specify response status,default 200
|
|
response_status = { type = "integer", default = 200, minimum = 100 },
|
|
-- specify response content type, support application/xml, text/plain
|
|
-- and application/json, default application/json
|
|
content_type = { type = "string", default = "application/json;charset=utf8" },
|
|
-- specify response body.
|
|
response_example = { type = "string" },
|
|
-- specify response json schema, if response_example is not nil, this conf will be ignore.
|
|
-- generate random response by json schema.
|
|
response_schema = { type = "object" },
|
|
with_mock_header = { type = "boolean", default = true },
|
|
response_headers = {
|
|
type = "object",
|
|
minProperties = 1,
|
|
patternProperties = {
|
|
["^[^:]+$"] = {
|
|
oneOf = {
|
|
{ type = "string" },
|
|
{ type = "number" }
|
|
}
|
|
}
|
|
},
|
|
}
|
|
},
|
|
anyOf = {
|
|
{ required = { "response_example" } },
|
|
{ required = { "response_schema" } }
|
|
}
|
|
}
|
|
|
|
local _M = {
|
|
version = 0.1,
|
|
priority = 10900,
|
|
name = "mocking",
|
|
schema = schema,
|
|
}
|
|
|
|
local function parse_content_type(content_type)
|
|
if not content_type then
|
|
return ""
|
|
end
|
|
local m = ngx_re.match(content_type, "([ -~]*);([ -~]*)", "jo")
|
|
if m and #m == 2 then
|
|
return m[1], m[2]
|
|
end
|
|
return content_type
|
|
end
|
|
|
|
|
|
function _M.check_schema(conf)
|
|
local ok, err = core.schema.check(schema, conf)
|
|
if not ok then
|
|
return false, err
|
|
end
|
|
|
|
local typ = parse_content_type(conf.content_type)
|
|
if not support_content_type[typ] then
|
|
return false, "unsupported content type!"
|
|
end
|
|
return true
|
|
end
|
|
|
|
|
|
local function gen_string(example)
|
|
if example and type(example) == "string" then
|
|
return example
|
|
end
|
|
local n = math.random(1, 10)
|
|
local list = {}
|
|
for i = 1, n do
|
|
table.insert(list, string.char(math.random(97, 122)))
|
|
end
|
|
return table.concat(list)
|
|
end
|
|
|
|
|
|
local function gen_number(example)
|
|
if example and type(example) == "number" then
|
|
return example
|
|
end
|
|
return math.random() * 10000
|
|
end
|
|
|
|
|
|
local function gen_integer(example)
|
|
if example and type(example) == "number" then
|
|
return math.floor(example)
|
|
end
|
|
return math.random(1, 10000)
|
|
end
|
|
|
|
|
|
local function gen_boolean(example)
|
|
if example and type(example) == "boolean" then
|
|
return example
|
|
end
|
|
local r = math.random(0, 1)
|
|
if r == 0 then
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
|
|
local gen_array, gen_object, gen_by_property
|
|
|
|
function gen_array(property)
|
|
local output = {}
|
|
if property.items == nil then
|
|
return nil
|
|
end
|
|
local v = property.items
|
|
local n = math.random(1, 3)
|
|
for i = 1, n do
|
|
table.insert(output, gen_by_property(v))
|
|
end
|
|
return output
|
|
end
|
|
|
|
|
|
function gen_object(property)
|
|
local output = {}
|
|
if not property.properties then
|
|
return output
|
|
end
|
|
for k, v in pairs(property.properties) do
|
|
output[k] = gen_by_property(v)
|
|
end
|
|
return output
|
|
end
|
|
|
|
|
|
function gen_by_property(property)
|
|
local typ = string.lower(property.type)
|
|
local example = property.example
|
|
|
|
if typ == "array" then
|
|
return gen_array(property)
|
|
end
|
|
|
|
if typ == "object" then
|
|
return gen_object(property)
|
|
end
|
|
|
|
if typ == "string" then
|
|
return gen_string(example)
|
|
end
|
|
|
|
if typ == "number" then
|
|
return gen_number(example)
|
|
end
|
|
|
|
if typ == "integer" then
|
|
return gen_integer(example)
|
|
end
|
|
|
|
if typ == "boolean" then
|
|
return gen_boolean(example)
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
|
|
function _M.access(conf, ctx)
|
|
local response_content = ""
|
|
|
|
if conf.response_example then
|
|
response_content = conf.response_example
|
|
else
|
|
local output = gen_object(conf.response_schema)
|
|
local typ = parse_content_type(conf.content_type)
|
|
if typ == "application/xml" or typ == "text/xml" then
|
|
response_content = xml2lua.toXml(output, "data")
|
|
|
|
elseif typ == "application/json" or typ == "text/plain" then
|
|
response_content = json.encode(output)
|
|
|
|
else
|
|
core.log.error("json schema body only support xml and json content type")
|
|
end
|
|
end
|
|
|
|
ngx.header["Content-Type"] = conf.content_type
|
|
if conf.with_mock_header then
|
|
ngx.header["x-mock-by"] = "APISIX/" .. core.version.VERSION
|
|
end
|
|
|
|
if conf.response_headers then
|
|
for key, value in pairs(conf.response_headers) do
|
|
value = core.utils.resolve_var(value, ctx.var)
|
|
core.response.add_header(key, value)
|
|
end
|
|
end
|
|
|
|
if conf.delay > 0 then
|
|
ngx.sleep(conf.delay)
|
|
end
|
|
return conf.response_status, core.utils.resolve_var(response_content, ctx.var)
|
|
end
|
|
|
|
return _M
|