This is an automated email from the ASF dual-hosted git repository. membphis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-apisix.git
The following commit(s) were added to refs/heads/master by this push: new ecd684b feat(admin api): enhance `PATCH` method, allow to update partial data. (#1609) ecd684b is described below commit ecd684b3a0b40b885aed4ced8339027eb189b38f Author: nic-chen <33000667+nic-c...@users.noreply.github.com> AuthorDate: Tue Jun 2 11:28:45 2020 +0800 feat(admin api): enhance `PATCH` method, allow to update partial data. (#1609) --- apisix/admin/global_rules.lua | 35 ++++++----------------------------- apisix/admin/routes.lua | 36 +++++++----------------------------- apisix/admin/services.lua | 35 ++++++----------------------------- apisix/admin/upstreams.lua | 35 ++++++----------------------------- apisix/core/table.lua | 19 +++++++++++++++++++ doc/admin-api-cn.md | 6 +++--- doc/admin-api.md | 6 +++--- t/admin/global-rules.t | 5 +++-- t/admin/routes.t | 29 ++++++++++++++++------------- t/admin/services.t | 23 ++++++++++++++--------- t/admin/upstream.t | 32 +++++++++++++++++++------------- 11 files changed, 102 insertions(+), 159 deletions(-) diff --git a/apisix/admin/global_rules.lua b/apisix/admin/global_rules.lua index c74d773..a29d953 100644 --- a/apisix/admin/global_rules.lua +++ b/apisix/admin/global_rules.lua @@ -104,19 +104,19 @@ function _M.delete(id) end -function _M.patch(id, conf, sub_path) +function _M.patch(id, conf) if not id then return 400, {error_msg = "missing global rule id"} end - if not sub_path then - return 400, {error_msg = "missing sub-path"} - end - if not conf then return 400, {error_msg = "missing new configuration"} end + if type(conf) ~= "table" then + return 400, {error_msg = "invalid configuration"} + end + local key = "/global_rules/" .. id local res_old, err = core.etcd.get(key) if not res_old then @@ -131,32 +131,9 @@ function _M.patch(id, conf, sub_path) core.json.delay_encode(res_old, true)) local node_value = res_old.body.node.value - local sub_value = node_value - local sub_paths = core.utils.split_uri(sub_path) - for i = 1, #sub_paths - 1 do - local sub_name = sub_paths[i] - if sub_value[sub_name] == nil then - sub_value[sub_name] = {} - end - sub_value = sub_value[sub_name] + node_value = core.table.merge(node_value, conf); - if type(sub_value) ~= "table" then - return 400, "invalid sub-path: /" - .. core.table.concat(sub_paths, 1, i) - end - end - - if type(sub_value) ~= "table" then - return 400, "invalid sub-path: /" .. sub_path - end - - local sub_name = sub_paths[#sub_paths] - if sub_name and sub_name ~= "" then - sub_value[sub_name] = conf - else - node_value = conf - end core.log.info("new conf: ", core.json.delay_encode(node_value, true)) local ok, err = check_conf(id, node_value, true) diff --git a/apisix/admin/routes.lua b/apisix/admin/routes.lua index bb7092f..6a14ce4 100644 --- a/apisix/admin/routes.lua +++ b/apisix/admin/routes.lua @@ -194,19 +194,19 @@ function _M.delete(id) end -function _M.patch(id, conf, sub_path, args) +function _M.patch(id, conf, args) if not id then return 400, {error_msg = "missing route id"} end - if not sub_path then - return 400, {error_msg = "missing sub-path"} - end - if not conf then return 400, {error_msg = "missing new configuration"} end + if type(conf) ~= "table" then + return 400, {error_msg = "invalid configuration"} + end + local key = "/routes" if id then key = key .. "/" .. id @@ -224,33 +224,11 @@ function _M.patch(id, conf, sub_path, args) core.log.info("key: ", key, " old value: ", core.json.delay_encode(res_old, true)) - local node_value = res_old.body.node.value - local sub_value = node_value - local sub_paths = core.utils.split_uri(sub_path) - for i = 1, #sub_paths - 1 do - local sub_name = sub_paths[i] - if sub_value[sub_name] == nil then - sub_value[sub_name] = {} - end - - sub_value = sub_value[sub_name] - if type(sub_value) ~= "table" then - return 400, "invalid sub-path: /" - .. core.table.concat(sub_paths, 1, i) - end - end + local node_value = res_old.body.node.value - if type(sub_value) ~= "table" then - return 400, "invalid sub-path: /" .. sub_path - end + node_value = core.table.merge(node_value, conf); - local sub_name = sub_paths[#sub_paths] - if sub_name and sub_name ~= "" then - sub_value[sub_name] = conf - else - node_value = conf - end core.log.info("new conf: ", core.json.delay_encode(node_value, true)) local id, err = check_conf(id, node_value, true) diff --git a/apisix/admin/services.lua b/apisix/admin/services.lua index e26ea41..7bebd3b 100644 --- a/apisix/admin/services.lua +++ b/apisix/admin/services.lua @@ -177,19 +177,19 @@ function _M.delete(id) end -function _M.patch(id, conf, sub_path) +function _M.patch(id, conf) if not id then return 400, {error_msg = "missing service id"} end - if not sub_path then - return 400, {error_msg = "missing sub-path"} - end - if not conf then return 400, {error_msg = "missing new configuration"} end + if type(conf) ~= "table" then + return 400, {error_msg = "invalid configuration"} + end + local key = "/services" .. "/" .. id local res_old, err = core.etcd.get(key) if not res_old then @@ -204,32 +204,9 @@ function _M.patch(id, conf, sub_path) core.json.delay_encode(res_old, true)) local new_value = res_old.body.node.value - local sub_value = new_value - local sub_paths = core.utils.split_uri(sub_path) - for i = 1, #sub_paths - 1 do - local sub_name = sub_paths[i] - if sub_value[sub_name] == nil then - sub_value[sub_name] = {} - end - sub_value = sub_value[sub_name] + new_value = core.table.merge(new_value, conf); - if type(sub_value) ~= "table" then - return 400, "invalid sub-path: /" - .. core.table.concat(sub_paths, 1, i) - end - end - - if type(sub_value) ~= "table" then - return 400, "invalid sub-path: /" .. sub_path - end - - local sub_name = sub_paths[#sub_paths] - if sub_name and sub_name ~= "" then - sub_value[sub_name] = conf - else - new_value = conf - end core.log.info("new value ", core.json.delay_encode(new_value, true)) local id, err = check_conf(id, new_value, true) diff --git a/apisix/admin/upstreams.lua b/apisix/admin/upstreams.lua index e989cd5..b7d2a04 100644 --- a/apisix/admin/upstreams.lua +++ b/apisix/admin/upstreams.lua @@ -213,19 +213,19 @@ function _M.delete(id) end -function _M.patch(id, conf, sub_path) +function _M.patch(id, conf) if not id then return 400, {error_msg = "missing upstream id"} end - if not sub_path then - return 400, {error_msg = "missing sub-path"} - end - if not conf then return 400, {error_msg = "missing new configuration"} end + if type(conf) ~= "table" then + return 400, {error_msg = "invalid configuration"} + end + local key = "/upstreams" .. "/" .. id local res_old, err = core.etcd.get(key) if not res_old then @@ -240,32 +240,9 @@ function _M.patch(id, conf, sub_path) core.json.delay_encode(res_old, true)) local new_value = res_old.body.node.value - local sub_value = new_value - local sub_paths = core.utils.split_uri(sub_path) - for i = 1, #sub_paths - 1 do - local sub_name = sub_paths[i] - if sub_value[sub_name] == nil then - sub_value[sub_name] = {} - end - sub_value = sub_value[sub_name] + new_value = core.table.merge(new_value, conf); - if type(sub_value) ~= "table" then - return 400, "invalid sub-path: /" - .. core.table.concat(sub_paths, 1, i) - end - end - - if type(sub_value) ~= "table" then - return 400, "invalid sub-path: /" .. sub_path - end - - local sub_name = sub_paths[#sub_paths] - if sub_name and sub_name ~= "" then - sub_value[sub_name] = conf - else - new_value = conf - end core.log.info("new value ", core.json.delay_encode(new_value, true)) local id, err = check_conf(id, new_value, true) diff --git a/apisix/core/table.lua b/apisix/core/table.lua index 14e0165..e666e16 100644 --- a/apisix/core/table.lua +++ b/apisix/core/table.lua @@ -85,5 +85,24 @@ local function deepcopy(orig) end _M.deepcopy = deepcopy +local ngx_null = ngx.null +local function merge(origin, extend) + for k,v in pairs(extend) do + if type(v) == "table" then + if type(origin[k] or false) == "table" then + merge(origin[k] or {}, extend[k] or {}) + else + origin[k] = v + end + elseif v == ngx_null then + origin[k] = nil + else + origin[k] = v + end + end + + return origin +end +_M.merge = merge return _M diff --git a/doc/admin-api-cn.md b/doc/admin-api-cn.md index 7a7d355..40303f4 100644 --- a/doc/admin-api-cn.md +++ b/doc/admin-api-cn.md @@ -40,7 +40,7 @@ |PUT |/apisix/admin/routes/{id}|{...}|根据 id 创建资源| |POST |/apisix/admin/routes |{...}|创建资源,id 由后台服务自动生成| |DELETE |/apisix/admin/routes/{id}|无|删除资源| -|PATCH |/apisix/admin/routes/{id}/{path}|{...}|修改已有 Route 的部分内容,其他不涉及部分会原样保留。| +|PATCH |/apisix/admin/routes/{id}|{...}|修改已有 Route 的部分内容,其他不涉及部分会原样保留;如果你要删除某个属性,将该属性的值设置为null 即可删除| > uri 请求参数: @@ -187,7 +187,7 @@ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13 |PUT |/apisix/admin/services/{id}|{...}|根据 id 创建资源| |POST |/apisix/admin/services |{...}|创建资源,id 由后台服务自动生成| |DELETE |/apisix/admin/services/{id}|无|删除资源| -|PATCH |/apisix/admin/services/{id}/{path}|{...}|修改已有 Service 的部分内容,其他不涉及部分会原样保留。| +|PATCH |/apisix/admin/services/{id}|{...}|修改已有 Service 的部分内容,其他不涉及部分会原样保留;如果你要删除某个属性,将该属性的值设置为null 即可删除| > body 请求参数: @@ -336,7 +336,7 @@ Date: Thu, 26 Dec 2019 08:17:49 GMT |PUT |/apisix/admin/upstreams/{id}|{...}|根据 id 创建资源| |POST |/apisix/admin/upstreams |{...}|创建资源,id 由后台服务自动生成| |DELETE |/apisix/admin/upstreams/{id}|无|删除资源| -|PATCH |/apisix/admin/upstreams/{id}/{path}|{...}|修改已有 Route 的部分内容,其他不涉及部分会原样保留。| +|PATCH |/apisix/admin/upstreams/{id}|{...}|修改已有 Route 的部分内容,其他不涉及部分会原样保留;如果你要删除某个属性,将该属性的值设置为null 即可删除| > body 请求参数: diff --git a/doc/admin-api.md b/doc/admin-api.md index f60ccb6..4d1cc9b 100644 --- a/doc/admin-api.md +++ b/doc/admin-api.md @@ -41,7 +41,7 @@ |PUT |/apisix/admin/routes/{id}|{...}|Create resource by ID| |POST |/apisix/admin/routes |{...}|Create resource, and ID is generated by server| |DELETE |/apisix/admin/routes/{id}|NULL|Remove resource| -|PATCH |/apisix/admin/routes/{id}/{path}|{...}|Update targeted content| +|PATCH |/apisix/admin/routes/{id}|{...}|Update targeted content, if you want to remove an attribute, set the attribute value to null to remove| > URI Request Parameters: @@ -183,7 +183,7 @@ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13 |PUT |/apisix/admin/services/{id}|{...}|Create resource by ID| |POST |/apisix/admin/services |{...}|Create resource, and ID is generated by server| |DELETE |/apisix/admin/services/{id}|NULL|Remove resource| -|PATCH |/apisix/admin/routes/{id}/{path}|{...}|Update targeted content| +|PATCH |/apisix/admin/routes/{id}|{...}|Update targeted content, if you want to remove an attribute, set the attribute value to null to remove| > Request Body Parameters: @@ -328,7 +328,7 @@ Return response from etcd currently. |PUT |/apisix/admin/upstreams/{id}|{...}|Create resource by ID| |POST |/apisix/admin/upstreams |{...}|Create resource, and ID is generated by server| |DELETE |/apisix/admin/upstreams/{id}|NULL|Remove resource| -|PATCH |/apisix/admin/upstreams/{id}/{path}|{...}|Update targeted content| +|PATCH |/apisix/admin/upstreams/{id}|{...}|Update targeted content, if you want to remove an attribute, set the attribute value to null to remove| > Request Body Parameters: diff --git a/t/admin/global-rules.t b/t/admin/global-rules.t index aa7a0c6..ee1f10c 100644 --- a/t/admin/global-rules.t +++ b/t/admin/global-rules.t @@ -164,16 +164,17 @@ passed location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/global_rules/1/plugins', + local code, body = t('/apisix/admin/global_rules/1', ngx.HTTP_PATCH, [[{ + "plugins": { "limit-count": { "count": 3, "time_window": 60, "rejected_code": 503, "key": "remote_addr" } - }]], + }}]], [[{ "node": { "value": { diff --git a/t/admin/routes.t b/t/admin/routes.t index 78ab5c6..ac8078b 100644 --- a/t/admin/routes.t +++ b/t/admin/routes.t @@ -993,9 +993,11 @@ passed location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/routes/1/methods', + local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PATCH, - '["GET"]', + [[{ + "methods": ["GET", null, null, null, null, null, null, null, null] + }]], [[{ "node": { "value": { @@ -1027,9 +1029,11 @@ passed location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/routes/1/uri', + local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PATCH, - '"/patch_test"', + [[{ + "uri": "/patch_test" + }]], [[{ "node": { "value": { @@ -1054,23 +1058,22 @@ passed -=== TEST 30: patch route(whole) +=== TEST 30: patch route(multi) --- config location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/routes/1/', + local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PATCH, [[{ "methods": ["GET"], "upstream": { "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" + "127.0.0.1:8080": null, + "127.0.0.2:8080": 1 + } }, - "desc": "new route", - "uri": "/index.html" + "desc": "new route" }]], [[{ "node": { @@ -1078,11 +1081,11 @@ passed "methods": [ "GET" ], - "uri": "/index.html", + "uri": "/patch_test", "desc": "new route", "upstream": { "nodes": { - "127.0.0.1:8080": 1 + "127.0.0.2:8080": 1 }, "type": "roundrobin" } diff --git a/t/admin/services.t b/t/admin/services.t index 6c6f503..a3aaa59 100644 --- a/t/admin/services.t +++ b/t/admin/services.t @@ -633,7 +633,7 @@ GET /t location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/services/1/', + local code, body = t('/apisix/admin/services/1', ngx.HTTP_PATCH, [[{ "upstream": { @@ -679,9 +679,11 @@ passed location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/services/1/desc', + local code, body = t('/apisix/admin/services/1', ngx.HTTP_PATCH, - '"new 19 service"', + [[{ + "desc": "new 19 service" + }]], [[{ "node": { "value": { @@ -717,20 +719,23 @@ passed location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/services/1/upstream', + local code, body = t('/apisix/admin/services/1', ngx.HTTP_PATCH, [[{ - "nodes": { - "127.0.0.1:8081": 3, - "127.0.0.1:8082": 4 - }, - "type": "roundrobin" + "upstream": { + "nodes": { + "127.0.0.1:8081": 3, + "127.0.0.1:8082": 4 + }, + "type": "roundrobin" + } }]], [[{ "node": { "value": { "upstream": { "nodes": { + "127.0.0.1:8080": 1, "127.0.0.1:8081": 3, "127.0.0.1:8082": 4 }, diff --git a/t/admin/upstream.t b/t/admin/upstream.t index 6ec7b22..661186d 100644 --- a/t/admin/upstream.t +++ b/t/admin/upstream.t @@ -652,7 +652,7 @@ GET /t location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/upstreams/1/', + local code, body = t('/apisix/admin/upstreams/1', ngx.HTTP_PATCH, [[{ "nodes": { @@ -694,9 +694,11 @@ passed location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/upstreams/1/desc', + local code, body = t('/apisix/admin/upstreams/1', ngx.HTTP_PATCH, - '"new 21 upstream"', + [[{ + "desc": "new 21 upstream" + }]], [[{ "node": { "value": { @@ -730,16 +732,19 @@ passed location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/upstreams/1/nodes', + local code, body = t('/apisix/admin/upstreams/1', ngx.HTTP_PATCH, [[{ - "127.0.0.1:8081": 3, - "127.0.0.1:8082": 4 + "nodes": { + "127.0.0.1:8081": 3, + "127.0.0.1:8082": 4 + } }]], [[{ "node": { "value": { "nodes": { + "127.0.0.1:8080": 1, "127.0.0.1:8081": 3, "127.0.0.1:8082": 4 }, @@ -768,18 +773,20 @@ passed location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/upstreams/1/nodes', + local code, body = t('/apisix/admin/upstreams/1', ngx.HTTP_PATCH, [[{ - "127.0.0.1:8081": 0, - "127.0.0.1:8082": 4 - }]], + "nodes": { + "127.0.0.1:8081": 3, + "127.0.0.1:8082": 0 + } + }]], [[{ "node": { "value": { "nodes": { - "127.0.0.1:8081": 0, - "127.0.0.1:8082": 4 + "127.0.0.1:8081": 3, + "127.0.0.1:8082": 0 }, "type": "roundrobin", "desc": "new 21 upstream" @@ -800,7 +807,6 @@ passed [error] - === TEST 24: set upstream(type: chash) --- config location /t {