feat(api): 添加备注功能以增强用例描述
- 在 Api 和 ApiCase 中新增 Note 方法,允许为用例设置可选备注 - 更新 TestRecord 结构体,包含备注字段以便记录用例信息 - 修改 Swagger 生成逻辑,支持备注字段的输出 - 更新文档,详细说明 Note 方法的使用及其在调试控制台的显示效果
This commit is contained in:
+10
-2
@@ -1,4 +1,4 @@
|
|||||||
# HoTime API 测试框架使用说明
|
# HoTime API 测试框架使用说明
|
||||||
|
|
||||||
不启动 HTTP 服务、自动事务回滚、链式 API、覆盖率报告、API 调试控制台生成。
|
不启动 HTTP 服务、自动事务回滚、链式 API、覆盖率报告、API 调试控制台生成。
|
||||||
|
|
||||||
@@ -209,6 +209,7 @@ func (app *TestApp) DB() *HoTimeDB
|
|||||||
| `a.Query(params)` | 设置 URL 查询参数 | `*ApiCase` |
|
| `a.Query(params)` | 设置 URL 查询参数 | `*ApiCase` |
|
||||||
| `a.Form(body)` | 设置 form 表单 body | `*ApiCase` |
|
| `a.Form(body)` | 设置 form 表单 body | `*ApiCase` |
|
||||||
| `a.File(field, name, content)` | 设置上传文件 | `*ApiCase` |
|
| `a.File(field, name, content)` | 设置上传文件 | `*ApiCase` |
|
||||||
|
| `a.Note(note)` | 为下一条用例设置备注,备注显示在调试控制台 | `*ApiCase` |
|
||||||
| `a.WithSession(s)` | 返回携带 session 的新 Api | `*Api` |
|
| `a.WithSession(s)` | 返回携带 session 的新 Api | `*Api` |
|
||||||
| `a.Get(desc, status, msg...)` | 直接 GET 请求(无数据场景) | `*ApiResponse` |
|
| `a.Get(desc, status, msg...)` | 直接 GET 请求(无数据场景) | `*ApiResponse` |
|
||||||
| `a.Post(desc, status, msg...)` | 直接 POST 请求(无数据场景) | `*ApiResponse` |
|
| `a.Post(desc, status, msg...)` | 直接 POST 请求(无数据场景) | `*ApiResponse` |
|
||||||
@@ -226,6 +227,7 @@ func (app *TestApp) DB() *HoTimeDB
|
|||||||
| `c.Query(params)` | 追加 URL 查询参数 | `*ApiCase` |
|
| `c.Query(params)` | 追加 URL 查询参数 | `*ApiCase` |
|
||||||
| `c.Form(body)` | 追加 form 字段 | `*ApiCase` |
|
| `c.Form(body)` | 追加 form 字段 | `*ApiCase` |
|
||||||
| `c.File(field, name, content)` | 设置上传文件 | `*ApiCase` |
|
| `c.File(field, name, content)` | 设置上传文件 | `*ApiCase` |
|
||||||
|
| `c.Note(note)` | 为本条用例添加可选备注,备注显示在调试控制台用例详情中 | `*ApiCase` |
|
||||||
| `c.WithSession(s)` | 覆盖本次请求的 session | `*ApiCase` |
|
| `c.WithSession(s)` | 覆盖本次请求的 session | `*ApiCase` |
|
||||||
| `c.Get(desc, status, msg...)` | 终端:发送 GET 请求并断言 | `*ApiResponse` |
|
| `c.Get(desc, status, msg...)` | 终端:发送 GET 请求并断言 | `*ApiResponse` |
|
||||||
| `c.Post(desc, status, msg...)` | 终端:发送 POST 请求并断言 | `*ApiResponse` |
|
| `c.Post(desc, status, msg...)` | 终端:发送 POST 请求并断言 | `*ApiResponse` |
|
||||||
@@ -303,6 +305,10 @@ a.JSON(Map{"name": "新名字"}).Put("更新", 0)
|
|||||||
// DELETE + URL 参数
|
// DELETE + URL 参数
|
||||||
a.Query(Map{"id": "5"}).Delete("删除", 0)
|
a.Query(Map{"id": "5"}).Delete("删除", 0)
|
||||||
|
|
||||||
|
// 带备注的用例(备注显示在调试控制台用例详情中,没有备注时不显示)
|
||||||
|
a.Form(Map{"phone": "138", "code": "1234"}).Note("sign = MD5(smsProxyKey+timestamp),smsProxyKey 见 config.json").Post("正常发送", 0)
|
||||||
|
a.Note("需先登录后在 Header 中携带 Authorization token").JSON(Map{"name": "新名字"}).Post("更新信息", 0)
|
||||||
|
|
||||||
// 校验响应中的具体字段
|
// 校验响应中的具体字段
|
||||||
resp := a.JSON(Map{"name": "138", "password": "123"}).Post("正常登录", 0)
|
resp := a.JSON(Map{"name": "138", "password": "123"}).Post("正常登录", 0)
|
||||||
if resp.GetBody().GetMap("result").GetString("token") == "" {
|
if resp.GetBody().GetMap("result").GetString("token") == "" {
|
||||||
@@ -408,6 +414,7 @@ func TestMain(m *testing.M) {
|
|||||||
"name": "正常登录",
|
"name": "正常登录",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"passed": true,
|
"passed": true,
|
||||||
|
"note": "备注内容(可选,未调用 .Note() 则不出现该字段)",
|
||||||
"query": {},
|
"query": {},
|
||||||
"json": { "name": "13800138000", "password": "123456" },
|
"json": { "name": "13800138000", "password": "123456" },
|
||||||
"response": { "status": 0, "msg": "ok", "data": {} }
|
"response": { "status": 0, "msg": "ok", "data": {} }
|
||||||
@@ -434,6 +441,7 @@ func TestMain(m *testing.M) {
|
|||||||
| `params[].in` | string | 参数位置:`query` / `form` / `json` |
|
| `params[].in` | string | 参数位置:`query` / `form` / `json` |
|
||||||
| `cases` | array | 实际运行的测试用例列表;若 `tested=false` 则为空数组 |
|
| `cases` | array | 实际运行的测试用例列表;若 `tested=false` 则为空数组 |
|
||||||
| `cases[].passed` | bool | 该用例是否通过(断言的 status/message 均匹配) |
|
| `cases[].passed` | bool | 该用例是否通过(断言的 status/message 均匹配) |
|
||||||
|
| `cases[].note` | string | 用例备注(可选),由 `.Note("...")` 设置,为空时不输出该字段 |
|
||||||
| `cases[].query` | object | 该用例的 Query 参数(GET 场景) |
|
| `cases[].query` | object | 该用例的 Query 参数(GET 场景) |
|
||||||
| `cases[].json` | object | 该用例的 JSON Body(`a.JSON(...)` 场景) |
|
| `cases[].json` | object | 该用例的 JSON Body(`a.JSON(...)` 场景) |
|
||||||
| `cases[].form` | object | 该用例的 Form 参数(`a.Form(...)` 场景) |
|
| `cases[].form` | object | 该用例的 Form 参数(`a.Form(...)` 场景) |
|
||||||
@@ -490,7 +498,7 @@ testApp.GenerateSwagger(
|
|||||||
| **三级导航** | 左侧侧边栏按 项目 > 控制器 > 方法 三级树形展示(一级默认展开,二级默认收起) |
|
| **三级导航** | 左侧侧边栏按 项目 > 控制器 > 方法 三级树形展示(一级默认展开,二级默认收起) |
|
||||||
| **搜索和筛选** | 支持关键字搜索,按 全部/已测试/未测试/通过/未通过 五种模式筛选 |
|
| **搜索和筛选** | 支持关键字搜索,按 全部/已测试/未测试/通过/未通过 五种模式筛选 |
|
||||||
| **左右分栏** | 接口信息栏右侧显示用例数量(`用例: N`);左侧为请求构建器,右侧为结果面板(两 tab 切换) |
|
| **左右分栏** | 接口信息栏右侧显示用例数量(`用例: N`);左侧为请求构建器,右侧为结果面板(两 tab 切换) |
|
||||||
| **测试用例 tab** | 右侧默认展示"测试用例"tab,手风琴列表(第一个默认展开);必填参数前显示红色 `*` 标记 |
|
| **测试用例 tab** | 右侧默认展示"测试用例"tab,手风琴列表(第一个默认展开);必填参数前显示红色 `*` 标记;用例设置了 `.Note(...)` 时在展开体和选中面板顶部显示灰色备注文字 |
|
||||||
| **发送结果 tab** | 点击"发送"后自动切换到"发送结果"tab 展示 cURL 命令和响应内容;可随时手动切回 |
|
| **发送结果 tab** | 点击"发送"后自动切换到"发送结果"tab 展示 cURL 命令和响应内容;可随时手动切回 |
|
||||||
| **必填参数推断** | 从测试用例自动推断必填参数(有 status==3 且值为空的用例),调试面板和用例展示均标记 `*` |
|
| **必填参数推断** | 从测试用例自动推断必填参数(有 status==3 且值为空的用例),调试面板和用例展示均标记 `*` |
|
||||||
| **预填用例** | 默认选中第一个用例自动填充左侧所有参数;"手动填写"置于列表末尾;必填字段有 `*` 标记 |
|
| **预填用例** | 默认选中第一个用例自动填充左侧所有参数;"手动填写"置于列表末尾;必填字段有 `*` 标记 |
|
||||||
|
|||||||
@@ -110,6 +110,13 @@ func (a *Api) Delete(desc string, status int, msg ...string) *ApiResponse {
|
|||||||
return a.newCase().Delete(desc, status, msg...)
|
return a.newCase().Delete(desc, status, msg...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note 为下一条用例设置备注,返回可继续链式的 ApiCase
|
||||||
|
func (a *Api) Note(note string) *ApiCase {
|
||||||
|
c := a.newCase()
|
||||||
|
c.note = note
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
// DB 获取数据库实例(在测试事务内)
|
// DB 获取数据库实例(在测试事务内)
|
||||||
func (a *Api) DB() *HoTimeDB {
|
func (a *Api) DB() *HoTimeDB {
|
||||||
return &a.app.Db
|
return &a.app.Db
|
||||||
@@ -134,6 +141,7 @@ type ApiCase struct {
|
|||||||
fileField string
|
fileField string
|
||||||
fileName string
|
fileName string
|
||||||
fileContent []byte
|
fileContent []byte
|
||||||
|
note string
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON 叠加 JSON body
|
// JSON 叠加 JSON body
|
||||||
@@ -180,6 +188,12 @@ func (c *ApiCase) WithSession(s Map) *ApiCase {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note 为本条用例添加可选备注,备注将显示在 swagger 调试控制台
|
||||||
|
func (c *ApiCase) Note(note string) *ApiCase {
|
||||||
|
c.note = note
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
// Get 终端操作:GET 请求 + 断言
|
// Get 终端操作:GET 请求 + 断言
|
||||||
func (c *ApiCase) Get(desc string, status int, msg ...string) *ApiResponse {
|
func (c *ApiCase) Get(desc string, status int, msg ...string) *ApiResponse {
|
||||||
return c.execute("GET", desc, status, msg...)
|
return c.execute("GET", desc, status, msg...)
|
||||||
@@ -247,6 +261,7 @@ func (c *ApiCase) execute(method, desc string, expectStatus int, expectMsg ...st
|
|||||||
Passed: passed,
|
Passed: passed,
|
||||||
Duration: duration,
|
Duration: duration,
|
||||||
Method: method,
|
Method: method,
|
||||||
|
Note: c.note,
|
||||||
}
|
}
|
||||||
if c.query != nil {
|
if c.query != nil {
|
||||||
record.Query = c.query
|
record.Query = c.query
|
||||||
|
|||||||
+12
-11
@@ -46,18 +46,19 @@ type MethodCoverage struct {
|
|||||||
|
|
||||||
// TestRecord 单条测试记录
|
// TestRecord 单条测试记录
|
||||||
type TestRecord struct {
|
type TestRecord struct {
|
||||||
Path string
|
Path string
|
||||||
Desc string
|
Desc string
|
||||||
CaseName string
|
CaseName string
|
||||||
Passed bool
|
Passed bool
|
||||||
Duration time.Duration
|
Duration time.Duration
|
||||||
Method string
|
Method string
|
||||||
Query map[string]interface{}
|
Query map[string]interface{}
|
||||||
JsonBody interface{}
|
JsonBody interface{}
|
||||||
FormBody map[string]interface{}
|
FormBody map[string]interface{}
|
||||||
HasFile bool
|
HasFile bool
|
||||||
FileField string
|
FileField string
|
||||||
ResponseBody map[string]interface{}
|
ResponseBody map[string]interface{}
|
||||||
|
Note string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestCollector 线程安全的测试记录收集器
|
// TestCollector 线程安全的测试记录收集器
|
||||||
|
|||||||
+15
-7
@@ -13,6 +13,7 @@ import (
|
|||||||
|
|
||||||
type swaggerTestCase struct {
|
type swaggerTestCase struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
Note string `json:"note,omitempty"`
|
||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
Passed bool `json:"passed"`
|
Passed bool `json:"passed"`
|
||||||
Query map[string]interface{} `json:"query,omitempty"`
|
Query map[string]interface{} `json:"query,omitempty"`
|
||||||
@@ -227,10 +228,10 @@ func (that *TestApp) GenerateSwagger(title, version, outputDir string) error {
|
|||||||
|
|
||||||
var cases []swaggerTestCase
|
var cases []swaggerTestCase
|
||||||
for _, r := range recs {
|
for _, r := range recs {
|
||||||
tc := swaggerTestCase{
|
tc := swaggerTestCase{
|
||||||
Name: r.CaseName, Method: r.Method, Passed: r.Passed,
|
Name: r.CaseName, Note: r.Note, Method: r.Method, Passed: r.Passed,
|
||||||
Response: r.ResponseBody,
|
Response: r.ResponseBody,
|
||||||
}
|
}
|
||||||
if r.Query != nil {
|
if r.Query != nil {
|
||||||
tc.Query = r.Query
|
tc.Query = r.Query
|
||||||
}
|
}
|
||||||
@@ -502,7 +503,7 @@ function show(){
|
|||||||
h+='<div class="split">';
|
h+='<div class="split">';
|
||||||
h+='<div class="split-l">'+buildLeft(e)+'</div>';
|
h+='<div class="split-l">'+buildLeft(e)+'</div>';
|
||||||
h+='<div class="split-r">';
|
h+='<div class="split-r">';
|
||||||
h+='<div class="rtabs"><div class="rtab on" id="rt0" onclick="rswitch(0)">测试用例'+(cnt?'('+cnt+')':'')+'</div><div class="rtab" id="rt1" onclick="rswitch(1)">发送结果</div></div>';
|
h+='<div class="rtabs"><div class="rtab on" id="rt0" onclick="rswitch(0)">用例结果</div><div class="rtab" id="rt1" onclick="rswitch(1)">发送结果</div></div>';
|
||||||
h+='<div class="rpn on" id="rp0"><div id="case-panel" style="padding:4px 0"></div></div>';
|
h+='<div class="rpn on" id="rp0"><div id="case-panel" style="padding:4px 0"></div></div>';
|
||||||
h+='<div class="rpn" id="rp1">'+respPanel()+'</div>';
|
h+='<div class="rpn" id="rp1">'+respPanel()+'</div>';
|
||||||
h+='</div></div>';
|
h+='</div></div>';
|
||||||
@@ -533,6 +534,7 @@ function cases(e){
|
|||||||
for(let i=0;i<e.cases.length;i++){const c=e.cases[i];
|
for(let i=0;i<e.cases.length;i++){const c=e.cases[i];
|
||||||
h+='<div class="cs"><div class="csh" onclick="accordion(this)"><span class="ar'+(i===0?' o':'')+'">▶</span><span class="dot '+(c.passed?'ok':'er')+'"></span>'+esc(c.name)+'<span class="ml">'+c.method+'</span></div>';
|
h+='<div class="cs"><div class="csh" onclick="accordion(this)"><span class="ar'+(i===0?' o':'')+'">▶</span><span class="dot '+(c.passed?'ok':'er')+'"></span>'+esc(c.name)+'<span class="ml">'+c.method+'</span></div>';
|
||||||
h+='<div class="csb'+(i===0?' o':'')+'"><div style="padding:8px 10px">';
|
h+='<div class="csb'+(i===0?' o':'')+'"><div style="padding:8px 10px">';
|
||||||
|
if(c.note)h+='<div style="color:#aaa;font-size:12px;margin-bottom:8px;line-height:1.5">备注: '+esc(c.note)+'</div>';
|
||||||
const curlId='cc'+i;const respId='cr'+i;
|
const curlId='cc'+i;const respId='cr'+i;
|
||||||
h+='<div class="row-hd" style="margin-bottom:4px"><span class="sec-title">cURL</span><button class="cp-sm" onclick="event.stopPropagation();copyEl(\''+curlId+'\')">复制</button></div>';
|
h+='<div class="row-hd" style="margin-bottom:4px"><span class="sec-title">cURL</span><button class="cp-sm" onclick="event.stopPropagation();copyEl(\''+curlId+'\')">复制</button></div>';
|
||||||
h+='<div class="curl" id="'+curlId+'">'+esc(caseCurl(e,c))+'</div>';
|
h+='<div class="curl" id="'+curlId+'">'+esc(caseCurl(e,c))+'</div>';
|
||||||
@@ -553,6 +555,7 @@ function accordion(el){
|
|||||||
|
|
||||||
function respPanel(){
|
function respPanel(){
|
||||||
let r='';
|
let r='';
|
||||||
|
r+='<div id="note-box" style="display:none;color:#aaa;font-size:12px;margin-bottom:8px;line-height:1.5"></div>';
|
||||||
r+='<div class="row-hd" style="margin-bottom:4px"><span class="sec-title">cURL</span><button class="cp-sm" onclick="copyEl(\'curl-box\')">复制</button></div>';
|
r+='<div class="row-hd" style="margin-bottom:4px"><span class="sec-title">cURL</span><button class="cp-sm" onclick="copyEl(\'curl-box\')">复制</button></div>';
|
||||||
r+='<div class="curl" id="curl-box">发送请求后生成</div>';
|
r+='<div class="curl" id="curl-box">发送请求后生成</div>';
|
||||||
r+='<div class="row-hd" style="margin:10px 0 4px"><span class="sec-title">响应 <span id="resp-st"></span></span><button class="cp-sm" onclick="copyEl(\'resp-body\')">复制</button></div>';
|
r+='<div class="row-hd" style="margin:10px 0 4px"><span class="sec-title">响应 <span id="resp-st"></span></span><button class="cp-sm" onclick="copyEl(\'resp-body\')">复制</button></div>';
|
||||||
@@ -567,7 +570,7 @@ function buildLeft(e){
|
|||||||
const at=document.getElementById('at').value;
|
const at=document.getElementById('at').value;
|
||||||
let l='';
|
let l='';
|
||||||
l+='<div style="display:flex;align-items:center;gap:6px;margin-bottom:10px">';
|
l+='<div style="display:flex;align-items:center;gap:6px;margin-bottom:10px">';
|
||||||
l+='<span style="color:var(--fg2);font-size:11px;white-space:nowrap">预填</span>';
|
l+='<span style="color:var(--fg2);font-size:11px;white-space:nowrap">测试用例选择</span>';
|
||||||
l+='<div class="pf-wrap"><button class="pf-btn" id="pf-btn" onclick="togglePfDd(event)"><span class="dot" id="pf-dot" style="display:none;flex-shrink:0"></span><span id="pf-label" style="flex:1;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fg2)">选择用例...</span><span style="color:var(--fg2);font-size:10px;flex-shrink:0">▾</span></button><ul class="pf-dd" id="pf-dd"></ul></div>';
|
l+='<div class="pf-wrap"><button class="pf-btn" id="pf-btn" onclick="togglePfDd(event)"><span class="dot" id="pf-dot" style="display:none;flex-shrink:0"></span><span id="pf-label" style="flex:1;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fg2)">选择用例...</span><span style="color:var(--fg2);font-size:10px;flex-shrink:0">▾</span></button><ul class="pf-dd" id="pf-dd"></ul></div>';
|
||||||
l+='<button class="sbtn" onclick="send()" id="sbtn">发送</button>';
|
l+='<button class="sbtn" onclick="send()" id="sbtn">发送</button>';
|
||||||
l+='</div>';
|
l+='</div>';
|
||||||
@@ -674,6 +677,7 @@ function updateCasePanel(e,i){
|
|||||||
if(i===''||!e?.cases?.[i]){panel.innerHTML='<div style="color:#555;padding:16px 10px">选择一个测试用例查看响应</div>';return;}
|
if(i===''||!e?.cases?.[i]){panel.innerHTML='<div style="color:#555;padding:16px 10px">选择一个测试用例查看响应</div>';return;}
|
||||||
const c=e.cases[i];
|
const c=e.cases[i];
|
||||||
let h='<div style="padding:8px 10px">';
|
let h='<div style="padding:8px 10px">';
|
||||||
|
if(c.note)h+='<div style="color:#aaa;font-size:12px;margin-bottom:8px;line-height:1.5">备注: '+esc(c.note)+'</div>';
|
||||||
h+='<div class="row-hd" style="margin-bottom:4px"><span class="sec-title">cURL</span><span class="'+(c.passed?'tg tg-ok':'tg tg-err')+'" style="margin-left:6px">'+(c.passed?'通过':'失败')+'</span><button class="cp-sm" onclick="copyEl(\'c-curl\')" style="margin-left:auto">复制</button></div>';
|
h+='<div class="row-hd" style="margin-bottom:4px"><span class="sec-title">cURL</span><span class="'+(c.passed?'tg tg-ok':'tg tg-err')+'" style="margin-left:6px">'+(c.passed?'通过':'失败')+'</span><button class="cp-sm" onclick="copyEl(\'c-curl\')" style="margin-left:auto">复制</button></div>';
|
||||||
h+='<div class="curl" id="c-curl">'+esc(caseCurl(e,c))+'</div>';
|
h+='<div class="curl" id="c-curl">'+esc(caseCurl(e,c))+'</div>';
|
||||||
h+='<div class="row-hd" style="margin:8px 0 4px"><span class="sec-title">响应</span><button class="cp-sm" onclick="copyEl(\'c-resp\')" style="margin-left:auto">复制</button></div>';
|
h+='<div class="row-hd" style="margin:8px 0 4px"><span class="sec-title">响应</span><button class="cp-sm" onclick="copyEl(\'c-resp\')" style="margin-left:auto">复制</button></div>';
|
||||||
@@ -697,11 +701,15 @@ function pfFill(v){
|
|||||||
if(C?.params){for(const p of C.params.filter(p=>p.in==='form'))addKVReq('f-rows',p.name,p.example!==undefined?String(p.example):'',p.required);}
|
if(C?.params){for(const p of C.params.filter(p=>p.in==='form'))addKVReq('f-rows',p.name,p.example!==undefined?String(p.example):'',p.required);}
|
||||||
addKV('f-rows');
|
addKV('f-rows');
|
||||||
}
|
}
|
||||||
document.getElementById('file-rows').innerHTML='';addFileRow();syncAuth();return;
|
document.getElementById('file-rows').innerHTML='';addFileRow();
|
||||||
|
const nb0=document.getElementById('note-box');if(nb0){nb0.style.display='none';nb0.textContent='';}
|
||||||
|
syncAuth();return;
|
||||||
}
|
}
|
||||||
const i=parseInt(v);
|
const i=parseInt(v);
|
||||||
if(isNaN(i)||i<0||!C?.cases?.[i])return;
|
if(isNaN(i)||i<0||!C?.cases?.[i])return;
|
||||||
const c=C.cases[i];
|
const c=C.cases[i];
|
||||||
|
const nb=document.getElementById('note-box');
|
||||||
|
if(nb){if(c.note){nb.textContent='备注: '+c.note;nb.style.display='block';}else{nb.textContent='';nb.style.display='none';}}
|
||||||
if(c.query){
|
if(c.query){
|
||||||
for(const[k,val]of Object.entries(c.query)){
|
for(const[k,val]of Object.entries(c.query)){
|
||||||
const req=C?.params?.find(p=>p.name===k&&p.in==='query')?.required||false;
|
const req=C?.params?.find(p=>p.name===k&&p.in==='query')?.required||false;
|
||||||
|
|||||||
Reference in New Issue
Block a user