-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: mocking plugin #5940
feat: mocking plugin #5940
Changes from all commits
befb95c
ff1b662
1c38b88
2f09041
c5c7449
ab7dd01
8b15af7
4a27281
cefcaf2
41df69a
f807386
f86fd49
0a02f27
a351ded
9848555
9190d27
27d6d1b
439cf14
0c296b5
0d92b43
5fb3647
a62f7a8
e66a680
db37e2d
71e9af1
a42a7fe
67e5496
ebfe46f
b894f95
1454033
6ff7f11
ce39d6c
7f900bb
08a0c55
6fb6b0c
8f374f3
514f0af
bebc44a
6fd29c6
e525a84
ed9ae66
10fdc1d
d693611
968fdb2
43ac0de
5a54066
288f7e0
49fdd59
7da4aa6
2f2cffc
f5d9529
9d511c6
f87bf28
65447a2
95c7678
e88b7d5
6f7a256
396d2a9
9e0b57b
e7cfd2f
45047e5
502a69b
b1b789c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,224 @@ | ||||||
-- | ||||||
-- 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 | ||||||
Drery marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
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 } | ||||||
}, | ||||||
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 | ||||||
|
||||||
Drery marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
function _M.check_schema(conf) | ||||||
local ok, err = core.schema.check(schema, conf) | ||||||
Drery marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
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 | ||||||
|
||||||
Drery marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
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) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @leslie-tsang There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hello there, Please refer to Non-Global Functions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@leslie-tsang It used to be that way.Thank you for your suggestion. |
||||||
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) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @leslie-tsang Hi, how can I run code checks in my own fork repository There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fork APISIX repo, and enable GitHub action. submit a PR to your own forks. |
||||||
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) | ||||||
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.delay > 0 then | ||||||
ngx.sleep(conf.delay) | ||||||
end | ||||||
return conf.response_status, core.utils.resolve_var(response_content) | ||||||
end | ||||||
|
||||||
return _M |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's put the
require
at the top, and sort all thexxx = xxx
localized imports by their names.