-
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 4 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,198 @@ | ||||||
-- | ||||||
-- 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 ngx = ngx | ||||||
local xml2lua = require("xml2lua") | ||||||
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. Let's put the |
||||||
|
||||||
local schema = { | ||||||
type = "object", | ||||||
properties = { | ||||||
-- specify response delay time,default 0ms | ||||||
delay = { type = "integer" }, | ||||||
-- specify response status,default 200 | ||||||
response_status = { type = "integer", default = 200, minimum = 1 }, | ||||||
Drery marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
-- specify response content type,support application/xml,text/plain and application/json,default application/json | ||||||
Drery marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
content_type = { type = "string", default = "application/json" }, | ||||||
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. Better to use 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.
@starsz because the content_type support two parts type and charset such as: "application/json" and "application/json;charset=utf8",so it's hard to define a enum~ |
||||||
-- specify response body. | ||||||
response_example = { type = "string" }, | ||||||
-- specify response json schema,if response_example is not nil,this conf will be ignore. | ||||||
Drery marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
-- generate random response by json schema. | ||||||
response_schema = { type = "object" }, | ||||||
}, | ||||||
anyOf = { | ||||||
{ required = { "response_example" } }, | ||||||
{ required = { "response_schema" } } | ||||||
} | ||||||
} | ||||||
|
||||||
local _M = { | ||||||
version = 0.1, | ||||||
priority = 9900, | ||||||
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. Don't forget to update the priority in the code |
||||||
name = "mocking", | ||||||
schema = schema, | ||||||
} | ||||||
|
||||||
local function parse_content_type(content_type) | ||||||
if not content_type then | ||||||
return "", "" | ||||||
end | ||||||
local sep_idx = string.find(content_type, ";") | ||||||
Drery marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
local type, charset | ||||||
if sep_idx then | ||||||
type = string.sub(content_type, 1, sep_idx - 1) | ||||||
charset = string.sub(content_type, sep_idx + 1) | ||||||
else | ||||||
type = content_type | ||||||
end | ||||||
return type, charset | ||||||
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 | ||||||
|
||||||
if conf.content_type == "" then | ||||||
conf.content_type = "application/json;charset=utf8" | ||||||
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. You can put this to schema define. |
||||||
end | ||||||
local type, _ = parse_content_type(conf.content_type) | ||||||
if type ~= "application/xml" and | ||||||
type ~= "application/json" and | ||||||
type ~= "text/plain" and | ||||||
type ~= "text/html" and | ||||||
type ~= "text/xml" then | ||||||
return false, "unsupported content type!" | ||||||
end | ||||||
return true | ||||||
end | ||||||
|
||||||
local function gen_string(example) | ||||||
if example ~= nil and type(example) == "string" then | ||||||
Drery marked this conversation as resolved.
Show resolved
Hide resolved
Drery marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
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 ~= nil and type(example) == "number" then | ||||||
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. same and take care of similar rest of them |
||||||
return example | ||||||
end | ||||||
return math.random() * 10000 | ||||||
end | ||||||
|
||||||
local function gen_integer(example) | ||||||
if example ~= nil and type(example) == "number" then | ||||||
return math.floor(example) | ||||||
end | ||||||
return math.random(1, 10000) | ||||||
end | ||||||
|
||||||
local function gen_boolean(example) | ||||||
if example ~= nil 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 function gen_base(property) | ||||||
local type = string.lower(property.type) | ||||||
local example = property.example | ||||||
if type == "string" then | ||||||
return gen_string(example) | ||||||
elseif type == "number" then | ||||||
return gen_number(example) | ||||||
elseif type == "integer" then | ||||||
return gen_integer(example) | ||||||
elseif type == "boolean" then | ||||||
return gen_boolean(example) | ||||||
end | ||||||
return nil | ||||||
end | ||||||
|
||||||
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) | ||||||
local type = string.lower(v.type) | ||||||
for i = 1, n do | ||||||
if type == "array" then | ||||||
table.insert(output, gen_array(v)) | ||||||
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. We can refactor this part and use a function that turns typ into the generated data. |
||||||
elseif type == "object" then | ||||||
table.insert(output, gen_object(v)) | ||||||
else | ||||||
table.insert(output, gen_base(v)) | ||||||
end | ||||||
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 property.properties == nil then | ||||||
Drery marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
return output | ||||||
end | ||||||
for k, v in pairs(property.properties) do | ||||||
local type = string.lower(v.type) | ||||||
if type == "array" then | ||||||
output[k] = gen_array(v) | ||||||
elseif type == "object" then | ||||||
output[k] = gen_object(v) | ||||||
else | ||||||
output[k] = gen_base(v) | ||||||
end | ||||||
end | ||||||
return output | ||||||
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 type, _ = parse_content_type(conf.content_type) | ||||||
if type == "application/xml" or type == "text/xml" then | ||||||
response_content = xml2lua.toXml(output, "data") | ||||||
elseif type == "application/json" or type == "text/plain" then | ||||||
response_content = core.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.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 sort the statements to make the
require
in a group.