This is an automated email from the ASF dual-hosted git repository.

spacewander pushed a commit to branch next
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/next by this push:
     new 83ee13b36 feat: response body format (#7366)
83ee13b36 is described below

commit 83ee13b363f94917562da1a3aed1f03aa800d79b
Author: tzssangglass <[email protected]>
AuthorDate: Tue Jul 5 09:13:48 2022 +0800

    feat: response body format (#7366)
---
 apisix/admin/utils.lua         |   4 +-
 apisix/core/etcd.lua           |  62 ++++++++++-
 conf/config-default.yaml       |   2 +
 t/admin/response_body_format.t | 237 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 298 insertions(+), 7 deletions(-)

diff --git a/apisix/admin/utils.lua b/apisix/admin/utils.lua
index 3ff695a47..db73dda67 100644
--- a/apisix/admin/utils.lua
+++ b/apisix/admin/utils.lua
@@ -24,8 +24,8 @@ local _M = {}
 
 local function inject_timestamp(conf, prev_conf, patch_conf)
     if not conf.create_time then
-        if prev_conf and prev_conf.node.value.create_time then
-            conf.create_time = prev_conf.node.value.create_time
+        if prev_conf and (prev_conf.node or prev_conf.list).value.create_time 
then
+            conf.create_time = (prev_conf.node or 
prev_conf.list).value.create_time
         else
             -- As we don't know existent data's create_time, we have to pretend
             -- they are created now.
diff --git a/apisix/core/etcd.lua b/apisix/core/etcd.lua
index a57a5d0c8..fd9eecc4a 100644
--- a/apisix/core/etcd.lua
+++ b/apisix/core/etcd.lua
@@ -21,6 +21,8 @@
 
 local fetch_local_conf  = require("apisix.core.config_local").local_conf
 local array_mt          = require("apisix.core.json").array_mt
+local try_read_attr     = require("apisix.core.table").try_read_attr
+local log               = require("apisix.core.log")
 local etcd              = require("resty.etcd")
 local clone_tab         = require("table.clone")
 local health_check      = require("resty.etcd.health_check")
@@ -35,6 +37,55 @@ local is_http = ngx.config.subsystem == "http"
 local _M = {}
 
 
+local admin_api_version
+local function is_v3()
+    if admin_api_version then
+        if admin_api_version == "v3" then
+            return true
+        end
+
+        if admin_api_version == "default" then
+            return false
+        end
+    end
+
+    local local_conf, err = fetch_local_conf()
+    if not local_conf then
+        admin_api_version = "default"
+        log.error("failed to fetch local conf: ", err)
+        return false
+    end
+
+    local api_ver = try_read_attr(local_conf, "apisix", "admin_api_version")
+    if api_ver ~= "v3" then
+        admin_api_version = "default"
+        return false
+    end
+
+    admin_api_version = api_ver
+    return true
+end
+
+
+local function to_v3(body, action)
+    if not is_v3() then
+        body.action = action
+    end
+end
+
+
+local function to_v3_list(body)
+    if not is_v3() then
+        return
+    end
+
+    if body.node.dir then
+        body.list = body.node.nodes
+        body.node = nil
+    end
+end
+
+
 -- this function create the etcd client instance used in the Admin API
 local function new()
     local local_conf, err = fetch_local_conf()
@@ -168,7 +219,7 @@ function _M.get_format(res, real_key, is_dir, formatter)
         return not_found(res)
     end
 
-    res.body.action = "get"
+    to_v3(res.body, "get")
 
     if formatter then
         return formatter(res)
@@ -196,6 +247,7 @@ function _M.get_format(res, real_key, is_dir, formatter)
     end
 
     res.body.kvs = nil
+    to_v3_list(res.body)
     return res
 end
 
@@ -272,7 +324,7 @@ local function set(key, value, ttl)
     res.headers["X-Etcd-Index"] = res.body.header.revision
 
     -- etcd v3 set would not return kv info
-    res.body.action = "set"
+    to_v3(res.body, "set")
     res.body.node = {}
     res.body.node.key = prefix .. key
     res.body.node.value = value
@@ -335,7 +387,7 @@ function _M.atomic_set(key, value, ttl, mod_revision)
 
     res.headers["X-Etcd-Index"] = res.body.header.revision
     -- etcd v3 set would not return kv info
-    res.body.action = "compareAndSwap"
+    to_v3(res.body, "compareAndSwap")
     res.body.node = {
         key = key,
         value = value,
@@ -373,7 +425,7 @@ function _M.push(key, value, ttl)
         return nil, err
     end
 
-    res.body.action = "create"
+    to_v3(res.body, "create")
     return res, nil
 end
 
@@ -397,7 +449,7 @@ function _M.delete(key)
     end
 
     -- etcd v3 set would not return kv info
-    res.body.action = "delete"
+    to_v3(res.body, "delete")
     res.body.node = {}
     res.body.key = prefix .. key
 
diff --git a/conf/config-default.yaml b/conf/config-default.yaml
index f35ec65b0..5f18c6255 100644
--- a/conf/config-default.yaml
+++ b/conf/config-default.yaml
@@ -85,6 +85,8 @@ apisix:
     admin_ssl_cert_key: ""      # Path of your self-signed server side key.
     admin_ssl_ca_cert: ""       # Path of your self-signed ca cert.The CA is 
used to sign all admin api callers' certificates.
 
+  #admin_api_version: v3        # The version of admin api, latest version is 
v3.
+
   # Default token when use API to call for Admin API.
   # *NOTE*: Highly recommended to modify this value to protect APISIX's Admin 
API.
   # Disabling this configuration item means that the Admin API does not
diff --git a/t/admin/response_body_format.t b/t/admin/response_body_format.t
new file mode 100644
index 000000000..246e78154
--- /dev/null
+++ b/t/admin/response_body_format.t
@@ -0,0 +1,237 @@
+#
+# 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.
+#
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_root_location();
+no_shuffle();
+log_level("info");
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    my $user_yaml_config = <<_EOC_;
+apisix:
+    node_listen: 1984
+    admin_key: null
+    admin_api_version: v3
+_EOC_
+    $block->set_value("yaml_config", $user_yaml_config);
+
+    if (!$block->request) {
+        $block->set_value("request", "GET /t");
+    }
+
+    if (!$block->no_error_log && !$block->error_log) {
+        $block->set_value("no_error_log", "[error]\n[alert]");
+    }
+});
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: use v3 admin api, no action in response body
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "methods": ["GET"],
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:8080": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "desc": "new route",
+                    "uri": "/index.html"
+                }]],
+                [[{
+                    "node": {
+                        "value": {
+                            "methods": [
+                                "GET"
+                            ],
+                            "uri": "/index.html",
+                            "desc": "new route",
+                            "upstream": {
+                                "nodes": {
+                                    "127.0.0.1:8080": 1
+                                },
+                                "type": "roundrobin"
+                            }
+                        },
+                        "key": "/apisix/routes/1"
+                    }
+                }]]
+                )
+
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 2: response body format only have count and list(count is 1)
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+            local code, message, res = t('/apisix/admin/routes',
+                ngx.HTTP_GET
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+
+            res = json.decode(res)
+            assert(res.count == #res.list)
+            assert(res.action == nil)
+            assert(res.node == nil)
+            assert(res.list.key == nil)
+            assert(res.list.dir == nil)
+            ngx.say(json.encode(res))
+        }
+    }
+--- response_body eval
+qr/\{"count":1,"list":\[\{.*\}\]/
+
+
+
+=== TEST 3: response body format only have count and list(count is 2)
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/2',
+                ngx.HTTP_PUT,
+                [[{
+                    "methods": ["GET"],
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:8080": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "desc": "new route",
+                    "uri": "/index.html"
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+            end
+
+            local code, message, res = t('/apisix/admin/routes',
+                ngx.HTTP_GET
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+
+            res = json.decode(res)
+            assert(res.count == #res.list)
+            assert(res.action == nil)
+            assert(res.node == nil)
+            assert(res.list.key == nil)
+            assert(res.list.dir == nil)
+            ngx.say(json.encode(res))
+        }
+    }
+--- response_body eval
+qr/\{"count":2,"list":\[\{.*\},\{.*\}\]/
+
+
+
+=== TEST 4: response body format(test services)
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/services/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "desc": "new service 001"
+                }]]
+                )
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+
+            local code, body = t('/apisix/admin/services/2',
+                 ngx.HTTP_PUT,
+                 [[{
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "desc": "new service 002"
+                }]]
+                )
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+
+            local code, message, res = t('/apisix/admin/services',
+                ngx.HTTP_GET
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+
+            res = json.decode(res)
+            assert(res.count == #res.list)
+            assert(res.action == nil)
+            assert(res.node == nil)
+            assert(res.list.key == nil)
+            assert(res.list.dir == nil)
+            ngx.say(json.encode(res))
+        }
+    }
+--- response_body eval
+qr/\{"count":2,"list":\[\{.*\},\{.*\}\]/

Reply via email to