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

juzhiyuan pushed a commit to branch v2.3-sync
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git

commit 6bc8c8ecaa742d796152a9d6a4bae9115d8580ba
Author: 琚致远 <juzhiy...@apache.org>
AuthorDate: Thu Jan 7 09:42:15 2021 +0800

    feat: added E2E test for plugins (#1214)
    
    * fix(FE): delete global plugin failed (#1170)
    
    * fix: delete global plugin failed
    
    * fix: filter disable plugins
    
    * fix: update online debug api protocol validation and error msg (#1166)
    
    * fix: update validation and msg
    
    * Update api/internal/handler/route_online_debug/route_online_debug.go
    
    * fix: update refer to code review
    
    Co-authored-by: 琚致远 <juzhiy...@apache.org>
    
    * feat(cli): Show GitHash for manager-api in branch v2.3 (backport #1162) 
(#1181)
    
    * fix: correct Version and GitHash output for manager-api command (#1162)
    
    * bug: fix Version and add GitHash for manager-api command
    
    Signed-off-by: imjoey <majunj...@gmail.com>
    
    * feat: git hash support generating .githash for apache release
    
    Signed-off-by: imjoey <majunj...@gmail.com>
    
    * feat: Add testcase for the new githash info
    
    Signed-off-by: imjoey <majunj...@gmail.com>
    
    * feat: add test case for .githash content validation
    
    Signed-off-by: imjoey <majunj...@gmail.com>
    
    * feat: Remove git command dependency for getting git hash
    
    Signed-off-by: imjoey <majunj...@gmail.com>
    
    * feat: set VERSION to 2.3 in branch v2.3
    
    Signed-off-by: imjoey <majunj...@gmail.com>
    
    * fix(fe): route search with status (#1205)
    
    * fix(fe): route search with status
    
    * fix: version and status select box allowclear
    
    * fix: remove console
    
    * fix: set create_time/update_time as omitempty (#1203)
    
    Signed-off-by: imjoey <majunj...@gmail.com>
    
    * fix(FE): service issues (#1209)
    
    * fix: omit checks when empty
    
    * fix: desc search
    
    * fix: omit checks when empty
    
    * feat: remove desc search
    
    * feat: add create service e2e test
    
    * feat: update code
    
    * feat: update code
    
    * chore: sync json schema from Apache APISIX 2.2 (#1177)
    
    * chore: sync json schema from Apache APISIX 2.2
    
    * fix: remove schema of plugins that not enable by default
    
    * fix test cases for plugin skywalking which is not enable by default
    
    * chore: expose port for control API
    
    * fix: control API config
    
    * fix yaml format
    
    * fix CI failed
    
    * fix: log path
    
    * fix: log path
    
    Co-authored-by: 琚致远 <juzhiy...@apache.org>
    
    * fix: well handle with malformed auth token in request header (#1206) 
(#1210)
    
    * fix: not panic if auth token is invalid
    
    Signed-off-by: imjoey <majunj...@gmail.com>
    
    * do not record the false in log
    
    Signed-off-by: imjoey <majunj...@gmail.com>
    
    Co-authored-by: Joey <majunj...@gmail.com>
    
    * fix: route list search query string (#1197)
    
    * fix: route list search qurey string
    
    * fix: well handle with malformed auth token in request header (#1206)
    
    * fix: not panic if auth token is invalid
    
    Signed-off-by: imjoey <majunj...@gmail.com>
    
    * do not record the false in log
    
    Signed-off-by: imjoey <majunj...@gmail.com>
    
    * feat: add search lables e2e
    
    * feat: add search route labels testcase
    
    * feat: update code
    
    * Update selector.json
    
    * Update search-route.spec.js
    
    Co-authored-by: Joey <majunj...@gmail.com>
    Co-authored-by: 琚致远 <juzhiy...@apache.org>
    
    * feat: init cypress with plugin
    
    * style: codes format
    
    * feat: added come testcases
    
    * feat: use the correct api version
    
    * feat: added tip
    
    * feat: added tip
    
    * feat: added test cases
    
    * feat: added disable
    
    * feat: added disable
    
    * feat: added disable
    
    * style: codes format
    
    * feat: added ajv formats
    
    * feat: remove useless codes
    
    Co-authored-by: litesun <su...@apache.org>
    Co-authored-by: liuxiran <belovedx...@126.com>
    Co-authored-by: Joey <majunj...@gmail.com>
    Co-authored-by: nic-chen <j...@163.com>
    Co-authored-by: nic-chen <33000667+nic-c...@users.noreply.github.com>
---
 web/cypress/fixtures/plugin-dataset.json           | 1347 ++++++++++++++++++++
 .../integration/plugin/schema-smocktest.spec.js    |   85 ++
 web/cypress/integration/route/search-route.spec.js |    5 +-
 .../service/create-and-delete-service.spec.js      |   10 +-
 .../upstream/create_and_delete_upstream.spec.js    |    2 +-
 web/package.json                                   |    3 +-
 web/src/components/Plugin/PluginDetail.tsx         |   50 +-
 web/src/components/Plugin/PluginPage.tsx           |    6 +-
 web/src/pages/Plugin/PluginMarket.tsx              |    2 +-
 web/src/pages/Plugin/service.ts                    |   12 +-
 web/src/pages/Route/List.tsx                       |    8 +-
 .../Route/components/DebugViews/DebugDrawView.tsx  |   67 +-
 web/src/pages/Route/constants.ts                   |   13 +-
 web/src/pages/Route/service.ts                     |    2 +-
 web/src/pages/Route/typing.d.ts                    |    2 +-
 web/src/pages/Service/Create.tsx                   |    9 +-
 web/src/pages/Service/List.tsx                     |    2 +-
 web/src/typings.d.ts                               |    1 +
 web/yarn.lock                                      |   15 +-
 19 files changed, 1566 insertions(+), 75 deletions(-)

diff --git a/web/cypress/fixtures/plugin-dataset.json 
b/web/cypress/fixtures/plugin-dataset.json
new file mode 100644
index 0000000..7f1ada3
--- /dev/null
+++ b/web/cypress/fixtures/plugin-dataset.json
@@ -0,0 +1,1347 @@
+{
+  "basic-auth": [
+    {
+      "type": "consumer",
+      "shouldValid": true,
+      "data": {
+        "username": "foo",
+        "password": "bar"
+      }
+    },
+    {
+      "type": "consumer",
+      "shouldValid": false,
+      "data": {
+        "username": 123,
+        "password": "bar"
+      }
+    },
+    {
+      "type": "consumer",
+      "shouldValid": false,
+      "data": {
+        "username": "foo"
+      }
+    },
+    {
+      "type": "consumer",
+      "shouldValid": false,
+      "data": {}
+    },
+    {
+      "type": "consumer",
+      "shouldValid": false,
+      "data": "blah"
+    },
+    {
+      "shouldValid": true,
+      "data": {}
+    }
+  ],
+  "hmac-auth": [
+    {
+      "shouldValid": true,
+      "data": {}
+    }
+  ],
+  "jwt-auth": [
+    {
+      "shouldValid": true,
+      "data": {}
+    }
+  ],
+  "key-auth": [
+    {
+      "shouldValid": true,
+      "data": {}
+    }
+  ],
+  "wolf-rbac": [
+    {
+      "shouldValid": true,
+      "data": {}
+    }
+  ],
+  "api-breaker": [
+    {
+      "shouldValid": true,
+      "data": {
+        "break_response_code": 502,
+        "unhealthy": {
+          "http_statuses": [500],
+          "failures": 1
+        },
+        "healthy": {
+          "http_statuses": [200],
+          "successes": 1
+        }
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "break_response_code": 502
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "break_response_code": 502,
+        "healthy": {}
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "break_response_code": 502,
+        "unhealthy": {}
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "break_response_code": 199,
+        "unhealthy": {
+          "http_statuses": [500, 503],
+          "failures": 3
+        },
+        "healthy": {
+          "http_statuses": [200, 206],
+          "successes": 3
+        }
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "break_response_code": 200,
+        "max_breaker_sec": -1
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "break_response_code": 200,
+        "max_breaker_sec": 40,
+        "unhealthy": {
+          "http_statuses": [500, 603],
+          "failures": 3
+        },
+        "healthy": {
+          "http_statuses": [200, 206],
+          "successes": 3
+        }
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "break_response_code": 500,
+        "unhealthy": {
+          "http_statuses": [500, 503],
+          "failures": 3
+        },
+        "healthy": {
+          "http_statuses": [206, 206],
+          "successes": 3
+        }
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "break_response_code": 599,
+        "unhealthy": {
+          "http_statuses": [500, 503],
+          "failures": 3
+        },
+        "healthy": {
+          "http_statuses": [200, 206],
+          "successes": 3
+        }
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "break_response_code": 502,
+        "unhealthy": {
+          "failures": 3
+        },
+        "healthy": {
+          "successes": 3
+        }
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "break_response_code": 502,
+        "max_breaker_sec": 10,
+        "unhealthy": {
+          "http_statuses": [500, 503],
+          "failures": 1
+        },
+        "healthy": {
+          "successes": 3
+        }
+      }
+    }
+  ],
+  "authz-keycloak": [
+    {
+      "shouldValid": true,
+      "data": {
+        "token_endpoint": 
"https://efactory-security-portal.salzburgresearch.at/";,
+        "grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "token_endpoint": 
"https://efactory-security-portal.salzburgresearch.at/";,
+        "permissions": ["res:customer#scopes:view"],
+        "timeout": 1000,
+        "audience": "University",
+        "grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket"
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "permissions": ["res:customer#scopes:view"]
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "token_endpoint": 
"http://127.0.0.1:8090/auth/realms/University/protocol/openid-connect/token";,
+        "permissions": ["course_resource#view"],
+        "audience": "course_management",
+        "grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
+        "timeout": 3000
+      }
+    }
+  ],
+  "batch-requests": [],
+  "consumer-restriction": [
+    {
+      "shouldValid": true,
+      "data": {
+        "title": "whitelist",
+        "whitelist": ["jack1", "jack2"]
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "whitelist": ["jack1"],
+        "blacklist": ["jack2"]
+      }
+    }
+  ],
+  "cors": [
+    {
+      "shouldValid": true,
+      "data": {
+        "allow_origins": "",
+        "allow_methods": "",
+        "allow_headers": "",
+        "expose_headers": "",
+        "max_age": 600,
+        "allow_credential": true
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "allow_origins": "",
+        "allow_methods": "",
+        "allow_headers": "",
+        "expose_headers": "",
+        "max_age": "600",
+        "allow_credential": true
+      }
+    }
+  ],
+  "echo": [],
+  "fault-injection": [
+    {
+      "shouldValid": false,
+      "data": {
+        "abort": {
+          "http_status": 100,
+          "body": "Fault Injection!\n"
+        }
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "abort": {}
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {}
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "delay": {}
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "delay": {
+          "duration": "test"
+        }
+      }
+    }
+  ],
+  "grpc-transcode": [],
+  "http-logger": [
+    {
+      "shouldValid": true,
+      "data": {
+        "uri": "http://127.0.0.1";
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "uri": "http://127.0.0.1";,
+        "auth_header": "Basic 123",
+        "timeout": 3,
+        "name": "http-logger",
+        "max_retry_count": 2,
+        "retry_delay": 2,
+        "buffer_duration": 2,
+        "inactive_timeout": 2,
+        "batch_max_size": 500
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "auth_header": "Basic 123",
+        "timeout": 3,
+        "name": "http-logger",
+        "max_retry_count": 2,
+        "retry_delay": 2,
+        "buffer_duration": 2,
+        "inactive_timeout": 2,
+        "batch_max_size": 500
+      }
+    }
+  ],
+  "ip-restriction": [
+    {
+      "shouldValid": true,
+      "data": {
+        "whitelist": ["10.255.254.0/24", "192.168.0.0/16"]
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "whitelist": ["10.255.256.0/24", "192.168.0.0/16"]
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "whitelist": ["10.255.254.0/38", "192.168.0.0/16"]
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {}
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "blacklist": []
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "whitelist": ["172.17.40.0/24"],
+        "blacklist": ["10.255.0.0/16"]
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "blacklist": ["::1", "fe80::/32"]
+      }
+    }
+  ],
+  "kafka-logger": [
+    {
+      "shouldValid": true,
+      "data": {
+        "kafka_topic": "test",
+        "key": "key1",
+        "broker_list": {
+          "127.0.0.1": 3
+        }
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "kafka_topic": "test",
+        "key": "key1"
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "kafka_topic": "test",
+        "key": "key1",
+        "broker_list": {
+          "127.0.0.1": 3000
+        },
+        "timeout": "10"
+      }
+    }
+  ],
+  "limit-conn": [
+    {
+      "shouldValid": true,
+      "data": {
+        "conn": 1,
+        "burst": 0,
+        "default_conn_delay": 0.1,
+        "rejected_code": 503,
+        "key": "remote_addr"
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "conn": 1,
+        "default_conn_delay": 0.1,
+        "rejected_code": 503,
+        "key": "remote_addr"
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "burst": 0,
+        "default_conn_delay": 0.1,
+        "rejected_code": 503,
+        "key": "remote_addr"
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "conn": -1,
+        "burst": 0,
+        "default_conn_delay": 0.1,
+        "rejected_code": 503,
+        "key": "remote_addr"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "conn": 100,
+        "burst": 50,
+        "default_conn_delay": 0.1,
+        "rejected_code": 503,
+        "key": "server_addr"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "conn": 5,
+        "burst": 1,
+        "default_conn_delay": 0.1,
+        "rejected_code": 503,
+        "key": "http_x_real_ip"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "conn": 5,
+        "burst": 1,
+        "default_conn_delay": 0.1,
+        "rejected_code": 503,
+        "key": "http_x_forwarded_for"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "conn": 2,
+        "burst": 1,
+        "default_conn_delay": 0.1,
+        "key": "remote_addr"
+      }
+    }
+  ],
+  "limit-count": [
+    {
+      "shouldValid": true,
+      "data": { "count": 2, "time_window": 60, "rejected_code": 503, "key": 
"remote_addr" }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "count": 2,
+        "time_window": 60,
+        "rejected_code": 503,
+        "key": "host"
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "time_window": 60,
+        "rejected_code": 503
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "count": -100,
+        "time_window": 60,
+        "rejected_code": 503,
+        "key": "remote_addr"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "count": 2,
+        "time_window": 60,
+        "rejected_code": 503,
+        "key": "server_addr"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "count": 2,
+        "time_window": 60,
+        "key": "remote_addr"
+      }
+    }
+  ],
+  "limit-req": [
+    {
+      "shouldValid": true,
+      "data": {
+        "rate": 1,
+        "burst": 0,
+        "rejected_code": 503,
+        "key": "remote_addr"
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "burst": 0,
+        "rejected_code": 503,
+        "key": "remote_addr"
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "rate": -1,
+        "burst": 0.1,
+        "rejected_code": 503,
+        "key": "remote_addr"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "rate": 1,
+        "burst": 0,
+        "key": "remote_addr"
+      }
+    }
+  ],
+  "openid-connect": [
+    {
+      "shouldValid": true,
+      "data": {
+        "client_id": "a",
+        "client_secret": "b",
+        "discovery": "c"
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "client_secret": "b",
+        "discovery": "c"
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "client_id": 123,
+        "client_secret": "b",
+        "discovery": "c"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "client_id": "kbyuFDidLLm280LIwVFiazOqjO3ty8KH",
+        "client_secret": 
"60Op4HFM0I8ajz0WdiStAbziZ-VFQttXuxixHHs2R7r7-CW8GR79l-mmLqMhc-Sa",
+        "discovery": "http://127.0.0.1:1980/.well-known/openid-configuration";,
+        "redirect_uri": "https://iresty.com";,
+        "ssl_verify": false,
+        "timeout": 10,
+        "scope": "apisix"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "discovery": 
"http://127.0.0.1:8090/auth/realms/University/.well-known/openid-configuration";,
+        "realm": "University",
+        "client_id": "course_management",
+        "client_secret": "d1ec69e9-55d2-4109-a3ea-befa071579d5",
+        "redirect_uri": "http://127.0.0.1:]] .. ngx.var.server_port .. 
[[/authenticated",
+        "ssl_verify": false,
+        "timeout": 10,
+        "introspection_endpoint_auth_method": "client_secret_post",
+        "introspection_endpoint": 
"http://127.0.0.1:8090/auth/realms/University/protocol/openid-connect/token/introspect";,
+        "set_access_token_header": true,
+        "access_token_in_authorization_header": false,
+        "set_id_token_header": true,
+        "set_userinfo_header": true
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "client_id": "kbyuFDidLLm280LIwVFiazOqjO3ty8KH",
+        "client_secret": 
"60Op4HFM0I8ajz0WdiStAbziZ-VFQttXuxixHHs2R7r7-CW8GR79l-mmLqMhc-Sa",
+        "discovery": 
"https://samples.auth0.com/.well-known/openid-configuration";,
+        "redirect_uri": "https://iresty.com";,
+        "ssl_verify": false,
+        "timeout": 10,
+        "bearer_only": true,
+        "scope": "apisix"
+      }
+    }
+  ],
+  "prometheus": [
+    {
+      "shouldValid": true,
+      "data": {}
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "invalid": "invalid"
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "invalid_property": 1
+      }
+    }
+  ],
+  "proxy-cache": [
+    {
+      "shouldValid": true,
+      "data": {
+        "cache_bypass": ["$arg_bypass"],
+        "cache_method": ["GET"],
+        "cache_http_status": [200],
+        "hide_cache_headers": true,
+        "no_cache": ["$arg_no_cache"]
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "cache_zone": "disk_cache_one",
+        "cache_bypass": ["$arg_bypass"],
+        "cache_method": "GET",
+        "cache_http_status": [200],
+        "hide_cache_headers": true,
+        "no_cache": ["$arg_no_cache"]
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "cache_zone": "disk_cache_one",
+        "cache_key": "${uri}-cache-key",
+        "cache_bypass": ["$arg_bypass"],
+        "cache_method": ["GET"],
+        "cache_http_status": [200],
+        "hide_cache_headers": true,
+        "no_cache": ["$arg_no_cache"]
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "cache_zone": "disk_cache_one",
+        "cache_bypass": "$arg_bypass",
+        "cache_method": ["GET"],
+        "cache_http_status": [200],
+        "hide_cache_headers": true,
+        "no_cache": ["$arg_no_cache"]
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "cache_zone": "disk_cache_one",
+        "cache_bypass": ["$arg_bypass"],
+        "cache_method": ["GET"],
+        "cache_http_status": [200],
+        "hide_cache_headers": true,
+        "no_cache": "$arg_no_cache"
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "cache_zone": "disk_cache_one",
+        "cache_key": ["$uri-", "-cache-id"],
+        "cache_bypass": ["$arg_bypass"],
+        "cache_method": ["GET"],
+        "cache_http_status": [200],
+        "hide_cache_headers": true,
+        "no_cache": ["$arg_no_cache"]
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "cache_zone": "disk_cache_one",
+        "cache_bypass": ["$arg_bypass"],
+        "cache_method": ["GET"],
+        "cache_http_status": [200],
+        "hide_cache_headers": true,
+        "no_cache": ["$arg_no_cache"]
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "cache_zone": "disk_cache_one",
+        "cache_bypass": ["$arg_bypass"],
+        "cache_method": ["GET"],
+        "cache_http_status": [200],
+        "hide_cache_headers": false,
+        "no_cache": ["$arg_no_cache"]
+      }
+    }
+  ],
+  "proxy-mirror": [
+    {
+      "shouldValid": false,
+      "data": {
+        "host": "127.0.0.1:1999"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "host": "http://127.0.0.1";
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "host": "http://127.0.0.1:1999/invalid_uri";
+      }
+    }
+  ],
+  "proxy-rewrite": [
+    {
+      "shouldValid": true,
+      "data": {
+        "uri": "/apisix/home",
+        "host": "apisix.apache.org",
+        "scheme": "http"
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "uri": "/apisix/home",
+        "host": "apisix.apache.org",
+        "scheme": "tcp"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "uri": "/uri/plugin_proxy_rewrite",
+        "headers": {
+          "X-Api-Version": "v2"
+        }
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "regex_uri": ["^/test/(.*)/(.*)/(.*)", "/$1_$2_$3"]
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "uri": "/hello",
+        "regex_uri": ["^/test/(.*)", "/${1}1"]
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "uri": "home"
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "uri": "/apisix/home",
+        "host": "apisix.apache.org",
+        "scheme": "http",
+        "invalid_att": "invalid"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "uri": "/uri",
+        "headers": {
+          "x-api": "$remote_addr",
+          "name": "$arg_name",
+          "x-key": "$http_key"
+        }
+      }
+    }
+  ],
+  "redirect": [
+    {
+      "shouldValid": true,
+      "data": {
+        "ret_code": 302,
+        "uri": "/foo"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "uri": "/foo"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "uri": "$uri/test/a${arg_name}c",
+        "ret_code": 302
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "uri": "/foo$$uri",
+        "ret_code": 302
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "uri": "\\$uri/foo$uri\\$uri/bar",
+        "ret_code": 301
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "uri": "https://$host$request_uri";,
+        "ret_code": 301
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "http_to_https": true
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "http_to_https": true,
+        "ret_code": 302
+      }
+    }
+  ],
+  "referer-restriction": [
+    {
+      "shouldValid": true,
+      "data": {
+        "whitelist": ["*.xx.com", "yy.com"]
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "bypass_missing": true,
+        "whitelist": ["*.xx.com", "yy.com"]
+      }
+    }
+  ],
+  "request-id": [
+    {
+      "shouldValid": true,
+      "data": {}
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "include_in_response": "bad_type"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "header_name": "Custom-Header-Name"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "include_in_response": true
+      }
+    }
+  ],
+  "response-rewrite": [
+    {
+      "shouldValid": true,
+      "data": {
+        "body": "Hello world",
+        "headers": {
+          "X-Server-id": 3
+        }
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "status_code": 599
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "body": 2,
+        "headers": {
+          "X-Server-id": "3"
+        }
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "headers": {
+          "X-Server-id": 3,
+          "X-Server-status": "on",
+          "Content-Type": ""
+        },
+        "body": "new body\n"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "body": "new body2\n"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "headers": {
+          "Location": "https://www.apache.org";
+        },
+        "status_code": 302
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "body": "SGVsbG8K",
+        "body_base64": true
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "body": "1",
+        "body_base64": true
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "headers": {
+          "X-Server-id": 3,
+          "X-Server-status": "on",
+          "Content-Type": ""
+        },
+        "body": "new body\n"
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "body": "Hello world",
+        "headers": {
+          "X-Server-id": 3
+        },
+        "invalid_att": "invalid"
+      }
+    }
+  ],
+  "serverless-post-function": [],
+  "serverless-pre-function": [],
+  "sls-logger": [
+    {
+      "shouldValid": true,
+      "data": {
+        "host": "cn-zhangjiakou-intranet.log.aliyuncs.com",
+        "port": 10009,
+        "project": "your-project",
+        "logstore": "your-logstore",
+        "access_key_id": "your_access_key",
+        "access_key_secret": "your_access_secret"
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "host": "cn-zhangjiakou-intranet.log.aliyuncs.com",
+        "port": 10009,
+        "project": "your-project",
+        "logstore": "your-logstore",
+        "access_key_id": "your_access_key",
+        "access_key_secret": "your_access_secret",
+        "timeout": "10"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "host": "100.100.99.135",
+        "port": 10009,
+        "project": "your_project",
+        "logstore": "your_logstore",
+        "access_key_id": "your_access_key_id",
+        "access_key_secret": "your_access_key_secret",
+        "timeout": 30000
+      }
+    }
+  ],
+  "syslog": [
+    {
+      "shouldValid": true,
+      "data": {
+        "host": "127.0.0.1",
+        "port": 3000
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": { "host": "127.0.0.1" }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "host": "127.0.0.1",
+        "port": "3000"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "host": "127.0.0.1",
+        "port": 5044
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "host": "127.0.0.1",
+        "port": 5044,
+        "flush_limit": 1,
+        "timeout": 1
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "host": "127.0.0.1",
+        "port": 5044,
+        "batch_max_size": 1
+      }
+    }
+  ],
+  "tcp-logger": [
+    {
+      "shouldValid": true,
+      "data": { "host": "127.0.0.1", "port": 3000 }
+    },
+    {
+      "shouldValid": false,
+      "data": { "port": 3000 }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "host": "127.0.0.1",
+        "port": 2000,
+        "timeout": "10",
+        "tls": false,
+        "tls_options": "tls options"
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "host": "127.0.0.1",
+        "port": 5044,
+        "tls": false
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "host": "312.0.0.1",
+        "port": 2000,
+        "batch_max_size": 1,
+        "max_retry_count": 2,
+        "retry_delay": 0
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "host": "127.0.0.1",
+        "port": 5044,
+        "tls": false,
+        "batch_max_size": 1
+      }
+    }
+  ],
+  "traffic-split": [
+    {
+      "shouldValid": true,
+      "data": {
+        "rules": [
+          {
+            "match": [
+              {
+                "vars": [
+                  ["arg_name", "==", "jack"],
+                  ["arg_age", "!", "<", "16"]
+                ]
+              },
+              {
+                "vars": [
+                  ["arg_name", "==", "rose"],
+                  ["arg_age", "!", ">", "32"]
+                ]
+              }
+            ],
+            "weighted_upstreams": [
+              {
+                "upstream": {
+                  "name": "upstream_A",
+                  "type": "roundrobin",
+                  "nodes": { "127.0.0.1:1981": 2 },
+                  "timeout": { "connect": 15, "send": 15, "read": 15 }
+                },
+                "weight": 2
+              },
+              {
+                "upstream": {
+                  "name": "upstream_B",
+                  "type": "roundrobin",
+                  "nodes": { "127.0.0.1:1982": 2 },
+                  "timeout": { "connect": 15, "send": 15, "read": 15 }
+                },
+                "weight": 2
+              },
+              {
+                "weight": 1
+              }
+            ]
+          }
+        ]
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "rules": [
+          {
+            "weighted_upstreams": [
+              {
+                "upstream": {
+                  "name": "upstream_A",
+                  "type": "roundrobin",
+                  "nodes": { "127.0.0.1:1981": 2 },
+                  "timeout": { "connect": 15, "send": 15, "read": 15 }
+                },
+                "weight": 2
+              },
+              {
+                "weight": 1
+              }
+            ]
+          }
+        ]
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "rules": [
+          {
+            "match": [
+              {
+                "vars": ["arg_name", 123, "jack"]
+              }
+            ],
+            "weighted_upstreams": [
+              {
+                "upstream": {
+                  "name": "upstream_A",
+                  "type": "roundrobin",
+                  "nodes": {
+                    "127.0.0.1:1981": 2
+                  },
+                  "timeout": { "connect": 15, "send": 15, "read": 15 }
+                },
+                "weight": 2
+              },
+              {
+                "weight": 1
+              }
+            ]
+          }
+        ]
+      }
+    }
+  ],
+  "udp-logger": [
+    {
+      "shouldValid": true,
+      "data": { "host": "127.0.0.1", "port": 3000 }
+    },
+    {
+      "shouldValid": false,
+      "data": { "port": 3000 }
+    },
+    {
+      "shouldValid": false,
+      "data": { "host": "127.0.0.1", "port": 3000, "timeout": "10" }
+    }
+  ],
+  "uri-blocker": [
+    {
+      "shouldValid": true,
+      "data": {
+        "block_rules": [".+("]
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "block_rules": ["^a", "^b"]
+      }
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "block_rules": ["aa"]
+      }
+    }
+  ],
+  "zipkin": [
+    {
+      "shouldValid": true,
+      "data": {
+        "endpoint": "http://127.0.0.1";,
+        "sample_ratio": 0.001
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "endpoint": "http://127.0.0.1";,
+        "sample_ratio": -0.1
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {
+        "endpoint": "http://127.0.0.1";,
+        "sample_ratio": 2
+      }
+    }
+  ],
+  "request-validation": [
+    {
+      "shouldValid": true,
+      "data": {
+        "body_schema": {}
+      }
+    },
+    {
+      "shouldValid": false,
+      "data": {}
+    },
+    {
+      "shouldValid": true,
+      "data": {
+        "body_schema": {
+          "type": "object",
+          "required": ["required_payload"],
+          "properties": {
+            "required_payload": { "type": "string" },
+            "boolean_payload": { "type": "boolean" },
+            "timeouts": {
+              "type": "integer",
+              "minimum": 1,
+              "maximum": 254,
+              "default": 3
+            },
+            "req_headers": {
+              "type": "array",
+              "minItems": 1,
+              "items": {
+                "type": "string"
+              }
+            }
+          }
+        }
+      }
+    }
+  ],
+  "mqtt-proxy": []
+}
diff --git a/web/cypress/integration/plugin/schema-smocktest.spec.js 
b/web/cypress/integration/plugin/schema-smocktest.spec.js
new file mode 100644
index 0000000..1878fbf
--- /dev/null
+++ b/web/cypress/integration/plugin/schema-smocktest.spec.js
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+/* eslint-disable no-undef */
+
+context('smoke test for plugin schema', () => {
+  beforeEach(() => {
+    cy.login();
+
+    cy.fixture('selector.json').as('selector');
+    cy.fixture('plugin-dataset.json').as('cases');
+  });
+
+  it('should visit plugin market', function () {
+    cy.visit('/');
+    cy.contains('Plugin').click();
+    cy.contains('Create').click();
+
+    const nameSelector = '[data-cy-plugin-name]';
+    cy.get(nameSelector).then(function (cards) {
+      [...cards].forEach((card) => {
+        const name = card.innerText;
+        const cases = this.cases[name] || [];
+        cases.forEach(({ shouldValid, data, type = '' }) => {
+          /**
+           * NOTE: This test is mainly for GlobalPlugin, which is using 
non-consumer-type schema.
+           */
+          if (type === 'consumer') {
+            return true;
+          }
+
+          cy.contains(name)
+            .parents('.ant-card-bordered')
+            .within(() => {
+              cy.contains('Enable').click({
+                force: true,
+              });
+            });
+
+          // NOTE: wait for the Drawer to appear on the DOM
+          cy.wait(800);
+          const switchSelector = '#disable';
+          cy.get(switchSelector).click();
+
+          cy.window().then(({ codemirror }) => {
+            if (codemirror) {
+              codemirror.setValue(JSON.stringify(data));
+            }
+          });
+
+          cy.contains('Submit').click();
+
+          // NOTE: wait for the HTTP call
+          cy.wait(500);
+          if (shouldValid) {
+            const drawerSelector = '.ant-drawer-content';
+            cy.get(drawerSelector).should('not.exist');
+          } else {
+            cy.get(this.selector.notification).should('contain', 'Invalid 
plugin data');
+
+            cy.get('.anticon-close').click({
+              multiple: true,
+            });
+            cy.contains('Cancel').click({
+              force: true,
+            });
+          }
+        });
+      });
+    });
+  });
+});
diff --git a/web/cypress/integration/route/search-route.spec.js 
b/web/cypress/integration/route/search-route.spec.js
index 287b248..89a635d 100644
--- a/web/cypress/integration/route/search-route.spec.js
+++ b/web/cypress/integration/route/search-route.spec.js
@@ -104,10 +104,7 @@ context('Create and Search Route', () => {
   it('should delete the route', function () {
     cy.visit('/routes/list');
     for (let i = 0; i < 3; i += 1) {
-      cy.contains(`test${i}`)
-        .siblings()
-        .contains('Delete')
-        .click();
+      cy.contains(`test${i}`).siblings().contains('Delete').click();
       cy.contains('button', 'Confirm').click();
       cy.get(this.domSelector.notification).should('contain', 'Delete Route 
Successfully');
       cy.wait(300);
diff --git a/web/cypress/integration/service/create-and-delete-service.spec.js 
b/web/cypress/integration/service/create-and-delete-service.spec.js
index 1536951..9ee46a6 100644
--- a/web/cypress/integration/service/create-and-delete-service.spec.js
+++ b/web/cypress/integration/service/create-and-delete-service.spec.js
@@ -17,14 +17,12 @@
 /* eslint-disable no-undef */
 
 context('create and delete service ', () => {
-
   beforeEach(() => {
     // init login
     cy.login();
   });
 
   it('should create service', () => {
-
     // go to create service page
     cy.visit('/');
     cy.contains('Service').click();
@@ -38,7 +36,7 @@ context('create and delete service ', () => {
     cy.contains('Next').click();
     cy.contains('Next').click();
     cy.contains('Submit').click();
-  })
+  });
 
   it('should delete the service', () => {
     cy.visit('/');
@@ -46,12 +44,10 @@ context('create and delete service ', () => {
 
     cy.get('[title=Name]').type('service');
     cy.contains('Search').click();
-    
+
     cy.contains('service').siblings().contains('Delete').click();
     cy.contains('button', 'Confirm').click();
-    cy.fixture('selector.json').then(({
-      notification
-    }) => {
+    cy.fixture('selector.json').then(({ notification }) => {
       cy.get(notification).should('contain', 'Delete Service Successfully');
     });
   });
diff --git 
a/web/cypress/integration/upstream/create_and_delete_upstream.spec.js 
b/web/cypress/integration/upstream/create_and_delete_upstream.spec.js
index f220677..b445f0c 100644
--- a/web/cypress/integration/upstream/create_and_delete_upstream.spec.js
+++ b/web/cypress/integration/upstream/create_and_delete_upstream.spec.js
@@ -21,7 +21,7 @@ context('Create and Delete Upstream', () => {
   const sleepTime = 100; // the unit is milliseconds
   const domSelectors = {
     notification: '.ant-notification-notice-message',
-    selectItem: '.ant-select-item-option-content'
+    selectItem: '.ant-select-item-option-content',
   };
 
   beforeEach(() => {
diff --git a/web/package.json b/web/package.json
index 9016a80..02e6552 100644
--- a/web/package.json
+++ b/web/package.json
@@ -55,7 +55,8 @@
     "@rjsf/antd": "2.2.0",
     "@rjsf/core": "2.2.0",
     "@uiw/react-codemirror": "^3.0.1",
-    "ajv": "^7.0.0-rc.2",
+    "ajv": "^7.0.3",
+    "ajv-formats": "^1.5.1",
     "antd": "^4.4.0",
     "base-64": "^1.0.0",
     "classnames": "^2.2.6",
diff --git a/web/src/components/Plugin/PluginDetail.tsx 
b/web/src/components/Plugin/PluginDetail.tsx
index b154985..ad76e03 100644
--- a/web/src/components/Plugin/PluginDetail.tsx
+++ b/web/src/components/Plugin/PluginDetail.tsx
@@ -15,13 +15,24 @@
  * limitations under the License.
  */
 import React, { useEffect, useRef } from 'react';
-import { Button, notification, PageHeader, Switch, Form, Select, Divider, 
Drawer, Alert } from 'antd';
+import {
+  Button,
+  notification,
+  PageHeader,
+  Switch,
+  Form,
+  Select,
+  Divider,
+  Drawer,
+  Alert,
+} from 'antd';
 import { useIntl } from 'umi';
 import CodeMirror from '@uiw/react-codemirror';
 import { js_beautify } from 'js-beautify';
 import { LinkOutlined } from '@ant-design/icons';
-
 import Ajv, { DefinedError } from 'ajv';
+import addFormats from 'ajv-formats';
+
 import { fetchSchema } from './service';
 
 type Props = {
@@ -29,7 +40,7 @@ type Props = {
   type?: 'global' | 'scoped';
   schemaType: PluginComponent.Schema;
   initialData: object;
-  pluginList: PluginComponent.Meta[],
+  pluginList: PluginComponent.Meta[];
   readonly?: boolean;
   visible: boolean;
   onClose?: () => void;
@@ -37,6 +48,7 @@ type Props = {
 };
 
 const ajv = new Ajv();
+addFormats(ajv);
 
 const FORM_ITEM_LAYOUT = {
   labelCol: {
@@ -69,17 +81,20 @@ const PluginDetail: React.FC<Props> = ({
   pluginList = [],
   readonly = false,
   initialData = {},
-  onClose = () => { },
-  onChange = () => { },
+  onClose = () => {},
+  onChange = () => {},
 }) => {
   const { formatMessage } = useIntl();
   const [form] = Form.useForm();
   const ref = useRef<any>(null);
   const data = initialData[name] || {};
-  const pluginType = pluginList.find(item => item.name === name)?.type
+  const pluginType = pluginList.find((item) => item.name === name)?.type;
 
   useEffect(() => {
-    form.setFieldsValue({ disable: initialData[name] && 
!initialData[name].disable });
+    form.setFieldsValue({
+      disable: initialData[name] && !initialData[name].disable,
+      scope: 'global',
+    });
   }, []);
 
   const validateData = (pluginName: string, value: PluginComponent.Data) => {
@@ -92,7 +107,6 @@ const PluginDetail: React.FC<Props> = ({
         } else {
           injectDisableProperty(schema);
         }
-
         const validate = ajv.compile(schema);
         if (validate(value)) {
           resolve(value);
@@ -198,8 +212,8 @@ const PluginDetail: React.FC<Props> = ({
           </Form.Item>
           {type === 'global' && (
             <Form.Item label="Scope" name="scope">
-              <Select disabled defaultValue="Global">
-                <Select.Option value="Global">Global</Select.Option>
+              <Select disabled>
+                <Select.Option value="global">Global</Select.Option>
               </Select>
             </Form.Item>
           )}
@@ -208,8 +222,12 @@ const PluginDetail: React.FC<Props> = ({
         <PageHeader
           title=""
           subTitle={
-            (pluginType === 'auth' && schemaType !== 'consumer') ? <Alert 
message={`${name} does not require configuration`} type="warning" />
-              : <>Current plugin: {name}</>}
+            pluginType === 'auth' && schemaType !== 'consumer' ? (
+              <Alert message={`${name} does not require configuration`} 
type="warning" />
+            ) : (
+              <>Current plugin: {name}</>
+            )
+          }
           ghost={false}
           extra={[
             <Button
@@ -228,7 +246,13 @@ const PluginDetail: React.FC<Props> = ({
           ]}
         />
         <CodeMirror
-          ref={ref}
+          ref={(codemirror) => {
+            ref.current = codemirror;
+            if (codemirror) {
+              // NOTE: for debug & test
+              window.codemirror = codemirror.editor;
+            }
+          }}
           value={JSON.stringify(data, null, 2)}
           options={{
             mode: 'json-ld',
diff --git a/web/src/components/Plugin/PluginPage.tsx 
b/web/src/components/Plugin/PluginPage.tsx
index f6d45a4..ec11987 100644
--- a/web/src/components/Plugin/PluginPage.tsx
+++ b/web/src/components/Plugin/PluginPage.tsx
@@ -117,8 +117,10 @@ const PluginPage: React.FC<Props> = ({
                     textAlign: 'center',
                   }}
                   title={[
-                    <div style={{ width: '100%', textAlign: 'center' }}>
-                      <span key={2}>{item.name}</span>
+                    <div style={{ width: '100%', textAlign: 'center' }} 
key={1}>
+                      <span key={2} data-cy-plugin-name={item.name}>
+                        {item.name}
+                      </span>
                     </div>,
                   ]}
                   style={{ height: 258, width: 200 }}
diff --git a/web/src/pages/Plugin/PluginMarket.tsx 
b/web/src/pages/Plugin/PluginMarket.tsx
index ac3a4e1..dadbdcf 100644
--- a/web/src/pages/Plugin/PluginMarket.tsx
+++ b/web/src/pages/Plugin/PluginMarket.tsx
@@ -32,7 +32,7 @@ const PluginMarket: React.FC = () => {
       });
       setInitialData(plugins);
     });
-  }
+  };
 
   useEffect(() => {
     initPageData();
diff --git a/web/src/pages/Plugin/service.ts b/web/src/pages/Plugin/service.ts
index a11e2ad..7c51d03 100644
--- a/web/src/pages/Plugin/service.ts
+++ b/web/src/pages/Plugin/service.ts
@@ -27,11 +27,13 @@ export const fetchList = (): Promise<{
       plugins: Record<string, any>;
     };
   }>(`/global_rules/${DEFAULT_GLOBAL_RULE_ID}`).then(({ data }) => {
-    const plugins = Object.entries(data.plugins || {}).filter(([, value]) => 
!value.disable).map(([name, value]) => ({
-      id: name,
-      name,
-      value,
-    }));
+    const plugins = Object.entries(data.plugins || {})
+      .filter(([, value]) => !value.disable)
+      .map(([name, value]) => ({
+        id: name,
+        name,
+        value,
+      }));
 
     return {
       data: plugins,
diff --git a/web/src/pages/Route/List.tsx b/web/src/pages/Route/List.tsx
index 466fdd6..43983aa 100644
--- a/web/src/pages/Route/List.tsx
+++ b/web/src/pages/Route/List.tsx
@@ -199,8 +199,12 @@ const Page: React.FC = () => {
 
         return (
           <Select style={{ width: '100%' }} allowClear>
-            <option key={RouteStatus.Offline} 
value={RouteStatus.Offline}>{formatMessage({ id: 'page.route.unpublished' 
})}</option>
-            <option key={RouteStatus.Publish} 
value={RouteStatus.Publish}>{formatMessage({ id: 'page.route.published' 
})}</option>
+            <option key={RouteStatus.Offline} value={RouteStatus.Offline}>
+              {formatMessage({ id: 'page.route.unpublished' })}
+            </option>
+            <option key={RouteStatus.Publish} value={RouteStatus.Publish}>
+              {formatMessage({ id: 'page.route.published' })}
+            </option>
           </Select>
         );
       },
diff --git a/web/src/pages/Route/components/DebugViews/DebugDrawView.tsx 
b/web/src/pages/Route/components/DebugViews/DebugDrawView.tsx
index 9180fe6..18b3d0e 100644
--- a/web/src/pages/Route/components/DebugViews/DebugDrawView.tsx
+++ b/web/src/pages/Route/components/DebugViews/DebugDrawView.tsx
@@ -53,7 +53,9 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = 
(props) => {
   const bodyCodeMirrorRef = useRef<any>(null);
   const [bodyType, setBodyType] = useState('none');
   const methodWithoutBody = ['GET', 'HEAD'];
-  const [bodyCodeMirrorMode, setBodyCodeMirrorMode] = 
useState(DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED[0].mode)
+  const [bodyCodeMirrorMode, setBodyCodeMirrorMode] = useState(
+    DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED[0].mode,
+  );
 
   enum DebugBodyType {
     None = 0,
@@ -84,7 +86,7 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = 
(props) => {
         transformDataForm = (formData || [])
           .filter((data) => data.check)
           .map((data) => {
-            return `${data.key}=${data.value}`
+            return `${data.key}=${data.value}`;
           });
 
         return transformDataForm.join('&');
@@ -265,37 +267,44 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> 
= (props) => {
             </TabPane>
             {showBodyTab && (
               <TabPane tab={formatMessage({ id: 
'page.route.TabPane.bodyParams' })} key="body">
-                <Radio.Group onChange={(e) => {setBodyType(e.target.value)}} 
value={bodyType}>
-                  {
-                    DEBUG_BODY_TYPE_SUPPORTED.map((type) => (
-                      <Radio value={type} key={type}>{type}</Radio>
-                    ))
-                  }
-                </Radio.Group>
-                {bodyType === 
DEBUG_BODY_TYPE_SUPPORTED[DebugBodyType.RawInput] && <Select
-                  size="small"
-                  onChange={(value) => {
-                    setBodyCodeMirrorMode(value)
+                <Radio.Group
+                  onChange={(e) => {
+                    setBodyType(e.target.value);
                   }}
-                  style={{ width: 100 }}
-                  defaultValue={bodyCodeMirrorMode}>
-                  {
-                    DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED.map((modeObj) =>(
-                    <Select.Option key={modeObj.name} 
value={modeObj.mode}>{modeObj.name}</Select.Option>
-                    ))
-                  }
-                </Select>}
-                <div style={{marginTop: 16}}>
-                  {
-                    (bodyType === 
DEBUG_BODY_TYPE_SUPPORTED[DebugBodyType.FormUrlencoded] || bodyType === 
DEBUG_BODY_TYPE_SUPPORTED[DebugBodyType.Json]) &&
+                  value={bodyType}
+                >
+                  {DEBUG_BODY_TYPE_SUPPORTED.map((type) => (
+                    <Radio value={type} key={type}>
+                      {type}
+                    </Radio>
+                  ))}
+                </Radio.Group>
+                {bodyType === 
DEBUG_BODY_TYPE_SUPPORTED[DebugBodyType.RawInput] && (
+                  <Select
+                    size="small"
+                    onChange={(value) => {
+                      setBodyCodeMirrorMode(value);
+                    }}
+                    style={{ width: 100 }}
+                    defaultValue={bodyCodeMirrorMode}
+                  >
+                    {DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED.map((modeObj) => (
+                      <Select.Option key={modeObj.name} value={modeObj.mode}>
+                        {modeObj.name}
+                      </Select.Option>
+                    ))}
+                  </Select>
+                )}
+                <div style={{ marginTop: 16 }}>
+                  {(bodyType === 
DEBUG_BODY_TYPE_SUPPORTED[DebugBodyType.FormUrlencoded] ||
+                    bodyType === 
DEBUG_BODY_TYPE_SUPPORTED[DebugBodyType.Json]) && (
                     <DebugParamsView form={bodyForm} />
-                  }
+                  )}
 
-                  {
-                    (bodyType === 
DEBUG_BODY_TYPE_SUPPORTED[DebugBodyType.RawInput]) &&
+                  {bodyType === 
DEBUG_BODY_TYPE_SUPPORTED[DebugBodyType.RawInput] && (
                     <Form>
                       <Form.Item>
-                          <CodeMirror
+                        <CodeMirror
                           ref={bodyCodeMirrorRef}
                           height={250}
                           options={{
@@ -310,7 +319,7 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = 
(props) => {
                         />
                       </Form.Item>
                     </Form>
-                  }
+                  )}
                 </div>
               </TabPane>
             )}
diff --git a/web/src/pages/Route/constants.ts b/web/src/pages/Route/constants.ts
index 22b2e61..10a083c 100644
--- a/web/src/pages/Route/constants.ts
+++ b/web/src/pages/Route/constants.ts
@@ -105,7 +105,16 @@ export const DEFAULT_DEBUG_AUTH_FORM_DATA = {
   authType: 'none',
 };
 
-export const DEBUG_BODY_TYPE_SUPPORTED: RouteModule.DebugBodyType[]= ['none', 
'x-www-form-urlencoded','json','raw input'];
+export const DEBUG_BODY_TYPE_SUPPORTED: RouteModule.DebugBodyType[] = [
+  'none',
+  'x-www-form-urlencoded',
+  'json',
+  'raw input',
+];
 
 // Note: codemirror mode: apl for text; javascript for json(need to format); 
xml for xml;
-export const DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED = [{name: 'Json', mode: 
'javascript'}, {name: 'Text', mode: 'apl'}, {name: 'XML', mode:'xml'}]
+export const DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED = [
+  { name: 'Json', mode: 'javascript' },
+  { name: 'Text', mode: 'apl' },
+  { name: 'XML', mode: 'xml' },
+];
diff --git a/web/src/pages/Route/service.ts b/web/src/pages/Route/service.ts
index 7ae96d0..95b7392 100644
--- a/web/src/pages/Route/service.ts
+++ b/web/src/pages/Route/service.ts
@@ -49,7 +49,7 @@ export const fetchList = ({ current = 1, pageSize = 10, 
...res }) => {
       label: labels.concat(API_VERSION).join(','),
       page: current,
       page_size: pageSize,
-      status
+      status,
     },
   }).then(({ data }) => {
     return {
diff --git a/web/src/pages/Route/typing.d.ts b/web/src/pages/Route/typing.d.ts
index 8ae302f..2d19fae 100644
--- a/web/src/pages/Route/typing.d.ts
+++ b/web/src/pages/Route/typing.d.ts
@@ -274,7 +274,7 @@ declare namespace RouteModule {
   type DebugBodyType = 'none' | 'x-www-form-urlencoded' | 'json' | 'raw input';
   type DebugDodyViewProps = {
     form: FormInstance;
-    changeBodyParamsType:(type: DebugBodyType) => void;
+    changeBodyParamsType: (type: DebugBodyType) => void;
     codeMirrorRef: any;
   };
   type DebugDrawProps = {
diff --git a/web/src/pages/Service/Create.tsx b/web/src/pages/Service/Create.tsx
index 15dc252..c85849d 100644
--- a/web/src/pages/Service/Create.tsx
+++ b/web/src/pages/Service/Create.tsx
@@ -133,7 +133,14 @@ const Page: React.FC = (props) => {
           {step === 2 && (
             <PluginPage initialData={plugins} onChange={setPlugins} 
schemaType="route" />
           )}
-          {step === 3 && <Preview upstreamForm={upstreamForm} 
upstreamRef={upstreamRef} form={form} plugins={plugins} />}
+          {step === 3 && (
+            <Preview
+              upstreamForm={upstreamForm}
+              upstreamRef={upstreamRef}
+              form={form}
+              plugins={plugins}
+            />
+          )}
         </Card>
       </PageHeaderWrapper>
       <ActionBar step={step} lastStep={3} onChange={onStepChange} 
withResultView />
diff --git a/web/src/pages/Service/List.tsx b/web/src/pages/Service/List.tsx
index 6fd28fc..ed17347 100644
--- a/web/src/pages/Service/List.tsx
+++ b/web/src/pages/Service/List.tsx
@@ -40,7 +40,7 @@ const Page: React.FC = () => {
     {
       title: formatMessage({ id: 'component.global.description' }),
       dataIndex: 'desc',
-      hideInSearch: true
+      hideInSearch: true,
     },
     {
       title: formatMessage({ id: 'component.global.operation' }),
diff --git a/web/src/typings.d.ts b/web/src/typings.d.ts
index 691ae14..ae6769a 100644
--- a/web/src/typings.d.ts
+++ b/web/src/typings.d.ts
@@ -43,6 +43,7 @@ interface Window {
     fieldsObject: GAFieldsObject | string,
   ) => void;
   reloadAuthorized: () => void;
+  codemirror: Record<string, any>;
 }
 
 declare let ga: Function;
diff --git a/web/yarn.lock b/web/yarn.lock
index 93ed598..dc98253 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -3833,6 +3833,13 @@ ajv-errors@^1.0.0:
   resolved 
"https://registry.npm.taobao.org/ajv-errors/download/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d";
   integrity sha1-81mGrOuRr63sQQL72FAUlQzvpk0=
 
+ajv-formats@^1.5.1:
+  version "1.5.1"
+  resolved 
"https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-1.5.1.tgz#0f301b1b3846182f224cc563fc0a032daafb7dab";
+  integrity 
sha512-s1RBVF4HZd2UjGkb6t6uWoXjf6o7j7dXPQIL7vprcIT/67bTD6+5ocsU0UKShS2qWxueGDWuGfKHfOxHWrlTQg==
+  dependencies:
+    ajv "^7.0.0"
+
 ajv-keywords@^3.1.0, ajv-keywords@^3.4.1:
   version "3.5.2"
   resolved 
"https://registry.npm.taobao.org/ajv-keywords/download/ajv-keywords-3.5.2.tgz?cache=0&sync_timestamp=1595906977498&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv-keywords%2Fdownload%2Fajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d";
@@ -3848,10 +3855,10 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, 
ajv@^6.12.3, ajv@^6.7.0, ajv@
     json-schema-traverse "^0.4.1"
     uri-js "^4.2.2"
 
-ajv@^7.0.0-rc.2:
-  version "7.0.0-rc.2"
-  resolved 
"https://registry.yarnpkg.com/ajv/-/ajv-7.0.0-rc.2.tgz#9c237b95072c1ee8c38e2df76422f37bacc9ae5e";
-  integrity 
sha512-D2iqHvbT3lszv5KSsTvJL9PSPf/2/s45i68vLXJmT124cxK/JOoOFyo/QnrgMKa2FHlVaMIsp1ZN1P4EH3bCKw==
+ajv@^7.0.0, ajv@^7.0.3:
+  version "7.0.3"
+  resolved 
"https://registry.yarnpkg.com/ajv/-/ajv-7.0.3.tgz#13ae747eff125cafb230ac504b2406cf371eece2";
+  integrity 
sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ==
   dependencies:
     fast-deep-equal "^3.1.1"
     json-schema-traverse "^1.0.0"

Reply via email to