You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by we...@apache.org on 2025/05/29 02:58:28 UTC
(apisix) branch master updated: feat: fill in the metadata of resource schema (#12224)
This is an automated email from the ASF dual-hosted git repository.
wenming pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new 3cb191ac8 feat: fill in the metadata of resource schema (#12224)
3cb191ac8 is described below
commit 3cb191ac87421594db5bcbe7a09793f15f61d694
Author: Zeping Bai <bz...@apache.org>
AuthorDate: Thu May 29 10:58:24 2025 +0800
feat: fill in the metadata of resource schema (#12224)
---
apisix/schema_def.lua | 104 ++++++++++++++++++---------
docs/en/latest/admin-api.md | 15 +++-
docs/zh/latest/admin-api.md | 6 ++
t/admin/metadata.spec.ts | 170 ++++++++++++++++++++++++++++++++++++++++++++
t/admin/metadata.t | 36 ++++++++++
t/ts/admin_api.ts | 13 +++-
6 files changed, 306 insertions(+), 38 deletions(-)
diff --git a/apisix/schema_def.lua b/apisix/schema_def.lua
index fd773990d..c543952dd 100644
--- a/apisix/schema_def.lua
+++ b/apisix/schema_def.lua
@@ -383,8 +383,15 @@ local private_key_schema = {
local upstream_schema = {
type = "object",
properties = {
+ -- metadata
+ id = id_schema,
+ name = rule_name_def,
+ desc = desc_def,
+ labels = labels_def,
create_time = timestamp_def,
update_time = timestamp_def,
+
+ -- properties
nodes = nodes_schema,
retries = {
type = "integer",
@@ -466,7 +473,6 @@ local upstream_schema = {
" For L4 proxy, it can be one of tcp/tls/udp." ..
" For specific protocols, it can be kafka."
},
- labels = labels_def,
discovery_type = {
description = "discovery type",
type = "string",
@@ -491,14 +497,11 @@ local upstream_schema = {
default = "pass"
},
upstream_host = host_def,
- name = rule_name_def,
- desc = desc_def,
service_name = {
type = "string",
maxLength = 256,
minLength = 1
},
- id = id_schema,
},
oneOf = {
{required = {"nodes"}},
@@ -542,8 +545,15 @@ _M.method_schema = method_schema
_M.route = {
type = "object",
properties = {
+ -- metadata
+ id = id_schema,
+ name = rule_name_def,
+ desc = desc_def,
+ labels = labels_def,
create_time = timestamp_def,
update_time = timestamp_def,
+
+ -- properties
uri = {type = "string", minLength = 1, maxLength = 4096},
uris = {
type = "array",
@@ -554,8 +564,6 @@ _M.route = {
minItems = 1,
uniqueItems = true,
},
- name = rule_name_def,
- desc = desc_def,
priority = {type = "integer", default = 0},
methods = {
@@ -596,8 +604,6 @@ _M.route = {
upstream = upstream_schema,
- labels = labels_def,
-
service_id = id_schema,
upstream_id = id_schema,
@@ -606,8 +612,6 @@ _M.route = {
type = "boolean",
},
- id = id_schema,
-
status = {
description = "route status, 1 to enable, 0 to disable",
type = "integer",
@@ -672,16 +676,19 @@ _M.route = {
_M.service = {
type = "object",
properties = {
+ -- metadata
id = id_schema,
- plugins = plugins_schema,
- upstream = upstream_schema,
- upstream_id = id_schema,
name = rule_name_def,
desc = desc_def,
labels = labels_def,
- script = {type = "string", minLength = 10, maxLength = 102400},
create_time = timestamp_def,
update_time = timestamp_def,
+
+ -- properties
+ plugins = plugins_schema,
+ upstream = upstream_schema,
+ upstream_id = id_schema,
+ script = {type = "string", minLength = 10, maxLength = 102400},
enable_websocket = {
description = "enable websocket for request",
type = "boolean",
@@ -700,16 +707,19 @@ _M.service = {
_M.consumer = {
type = "object",
properties = {
+ -- metadata
username = {
type = "string", minLength = 1, maxLength = rule_name_def.maxLength,
pattern = [[^[a-zA-Z0-9_]+$]]
},
- group_id = id_schema,
- plugins = plugins_schema,
+ desc = desc_def,
labels = labels_def,
create_time = timestamp_def,
update_time = timestamp_def,
- desc = desc_def,
+
+ -- properties
+ group_id = id_schema,
+ plugins = plugins_schema,
},
required = {"username"},
additionalProperties = false,
@@ -718,16 +728,21 @@ _M.consumer = {
_M.credential = {
type = "object",
properties = {
+ -- metadata
id = id_schema,
+ name = rule_name_def,
+ desc = desc_def,
+ labels = labels_def,
+ create_time = timestamp_def,
+ update_time = timestamp_def,
+
+ -- properties
plugins = {
type = "object",
maxProperties = 1,
},
- labels = labels_def,
- create_time = timestamp_def,
- update_time = timestamp_def,
- desc = desc_def,
},
+ additionalProperties = false,
}
_M.upstream = upstream_schema
@@ -742,7 +757,14 @@ local secret_uri_schema = {
_M.ssl = {
type = "object",
properties = {
+ -- metadata
id = id_schema,
+ desc = desc_def,
+ labels = labels_def,
+ create_time = timestamp_def,
+ update_time = timestamp_def,
+
+ -- properties
type = {
description = "ssl certificate type, " ..
"server to server certificate, " ..
@@ -814,7 +836,6 @@ _M.ssl = {
},
required = {"ca"},
},
- labels = labels_def,
status = {
description = "ssl status, 1 to enable, 0 to disable",
type = "integer",
@@ -830,8 +851,6 @@ _M.ssl = {
enum = {"TLSv1.1", "TLSv1.2", "TLSv1.3"}
},
},
- create_time = timestamp_def,
- update_time = timestamp_def
},
["if"] = {
properties = {
@@ -852,13 +871,20 @@ _M.ssl = {
+-- TODO: Design a plugin resource registration framework used by plugins and move the proto
+-- resource to grpc-transcode plugin, which should not be an APISIX core resource
_M.proto = {
type = "object",
properties = {
+ -- metadata
id = id_schema,
+ name = rule_name_def,
desc = desc_def,
+ labels = labels_def,
create_time = timestamp_def,
update_time = timestamp_def,
+
+ -- properties
content = {
type = "string", minLength = 1, maxLength = 1024*1024
}
@@ -871,10 +897,13 @@ _M.proto = {
_M.global_rule = {
type = "object",
properties = {
+ -- metadata
id = id_schema,
- plugins = plugins_schema,
create_time = timestamp_def,
- update_time = timestamp_def
+ update_time = timestamp_def,
+
+ -- properties
+ plugins = plugins_schema,
},
required = {"id", "plugins"},
additionalProperties = false,
@@ -923,12 +952,16 @@ local xrpc_protocol_schema = {
_M.stream_route = {
type = "object",
properties = {
+ -- metadata
id = id_schema,
+ name = rule_name_def,
desc = desc_def,
+ labels = labels_def,
create_time = timestamp_def,
update_time = timestamp_def,
+
+ -- properties
remote_addr = remote_addr_def,
- labels = labels_def, -- The ingress project need this field
server_addr = {
description = "server IP",
type = "string",
@@ -977,15 +1010,18 @@ _M.plugins = {
_M.plugin_config = {
type = "object",
properties = {
+ -- metadata
+ id = id_schema,
name = {
type = "string",
},
- id = id_schema,
desc = desc_def,
- plugins = plugins_schema,
labels = labels_def,
create_time = timestamp_def,
- update_time = timestamp_def
+ update_time = timestamp_def,
+
+ -- properties
+ plugins = plugins_schema,
},
required = {"id", "plugins"},
additionalProperties = false,
@@ -995,12 +1031,16 @@ _M.plugin_config = {
_M.consumer_group = {
type = "object",
properties = {
+ -- metadata
id = id_schema,
+ name = rule_name_def,
desc = desc_def,
- plugins = plugins_schema,
labels = labels_def,
create_time = timestamp_def,
- update_time = timestamp_def
+ update_time = timestamp_def,
+
+ -- properties
+ plugins = plugins_schema,
},
required = {"id", "plugins"},
additionalProperties = false,
diff --git a/docs/en/latest/admin-api.md b/docs/en/latest/admin-api.md
index e6099e65a..f75a82037 100644
--- a/docs/en/latest/admin-api.md
+++ b/docs/en/latest/admin-api.md
@@ -891,6 +891,7 @@ Credential resource request address:/apisix/admin/consumers/{username}/credent
| Parameter | Required | Type | Description | Example |
| ----------- |-----| ------- |------------------------------------------------------------|-------------------------------------------------|
| plugins | False | Plugin | Auth plugins configuration. | |
+| name | False | Auxiliary | Identifier for the Credential. | credential_primary |
| desc | False | Auxiliary | Description of usage scenarios. | credential xxxx |
| labels | False | Match Rules | Attributes of the Credential specified as key-value pairs. | {"version":"v2","build":"16","env":"production"} |
@@ -1275,6 +1276,7 @@ For notes on ID syntax please refer to: [ID Syntax](#quick-note-on-id-syntax)
| client.depth | False | Certificate | Sets the verification depth in client certificate chains. Defaults to 1. Requires OpenResty 1.19+. | |
| client.skip_mtls_uri_regex | False | An array of regular expressions, in PCRE format | Used to match URI, if matched, this request bypasses the client certificate checking, i.e. skip the MTLS. | ["/hello[0-9]+", "/foobar"] |
| snis | True, only if `type` is `server` | Match Rules | A non-empty array of HTTPS SNI | |
+| desc | False | Auxiliary | Description of usage scenarios. | certs for production env |
| labels | False | Match Rules | Attributes of the resource specified as key-value pairs. | {"version":"v2","build":"16","env":"production"} |
| type | False | Auxiliary | Identifies the type of certificate, default `server`. | `client` Indicates that the certificate is a client certificate, which is used when APISIX accesses the upstream; `server` Indicates that the certificate is a server-side certificate, which is used by APISIX when verifying client requests. |
| status | False | Auxiliary | Enables the current SSL. Set to `1` (enabled) by default. | `1` to enable, `0` to disable |
@@ -1342,6 +1344,7 @@ Consumer group resource request address: /apisix/admin/consumer_groups/{id}
| Parameter | Required | Description | Example |
| ----------- | -------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------ |
| plugins | True | Plugins that are executed during the request/response cycle. See [Plugin](terminology/plugin.md) for more. | |
+| name | False | Identifier for the consumer group. | premium-tier |
| desc | False | Description of usage scenarios. | customer xxxx |
| labels | False | Attributes of the Consumer group specified as key-value pairs. | {"version":"v2","build":"16","env":"production"} |
@@ -1491,6 +1494,9 @@ Stream Route resource request address: /apisix/admin/stream_routes/{id}
| Parameter | Required | Type | Description | Example |
| ----------- | -------- | -------- | ------------------------------------------------------------------- | ----------------------------- |
+| name | False | Auxiliary | Identifier for the Stream Route. | postgres-proxy |
+| desc | False | Auxiliary | Description of usage scenarios. | proxy endpoint for postgresql |
+| labels | False | Match Rules | Attributes of the Proto specified as key-value pairs. | {"version":"17","service":"user","env":"production"} |
| upstream | False | Upstream | Configuration of the [Upstream](./terminology/upstream.md). | |
| upstream_id | False | Upstream | Id of the [Upstream](terminology/upstream.md) service. | |
| service_id | False | String | Id of the [Service](terminology/service.md) service. | |
@@ -1684,9 +1690,12 @@ Proto resource request address: /apisix/admin/protos/{id}
### Request Body Parameters
-| Parameter | Required | Type | Description | Example |
-|-----------|----------|---------|--------------------------------------| ----------------------------- |
-| content | True | String | content of `.proto` or `.pb` files | See [here](./plugins/grpc-transcode.md#enabling-the-plugin) |
+| Parameter | Required | Type | Description | Example |
+|-----------|----------|-----------|--------------------------------------| ----------------------------- |
+| content | True | String | Content of `.proto` or `.pb` files | See [here](./plugins/grpc-transcode.md#enabling-the-plugin) |
+| name | False | Auxiliary | Identifier for the Protobuf definition. | user-proto |
+| desc | False | Auxiliary | Description of usage scenarios. | protobuf for user service |
+| labels | False | Match Rules | Attributes of the Proto specified as key-value pairs. | {"version":"v2","service":"user","env":"production"} |
## Schema validation
diff --git a/docs/zh/latest/admin-api.md b/docs/zh/latest/admin-api.md
index 0bc5a963c..8f78fa16d 100644
--- a/docs/zh/latest/admin-api.md
+++ b/docs/zh/latest/admin-api.md
@@ -904,6 +904,7 @@ Credential 资源请求地址:/apisix/admin/consumers/{username}/credentials/{
| 名称 | 必选项 | 类型 | 描述 | 示例值 |
| ----------- |-----| ------- |-----------------------| ------------------------------------------------ |
| plugins | 是 | Plugin | 该 Credential 对应的插件配置。 | |
+| name | 否 | 辅助 | 消费者 Credential 名 | credential_primary |
| desc | 否 | 辅助 | Credential 描述。 | |
| labels | 否 | 匹配规则 | 标识附加属性的键值对。 | {"version":"v2","build":"16","env":"production"} |
@@ -1275,6 +1276,7 @@ SSL 资源请求地址:/apisix/admin/ssls/{id}
| client.depth | 否 | 辅助 | 设置客户端证书校验的深度,默认为 1。该特性需要 OpenResty 为 1.19 及以上版本。 | |
| client.skip_mtls_uri_regex | 否 | PCRE 正则表达式数组 | 用来匹配请求的 URI,如果匹配,则该请求将绕过客户端证书的检查,也就是跳过 MTLS。 | ["/hello[0-9]+", "/foobar"] |
| snis | 是 | 匹配规则 | 非空数组形式,可以匹配多个 SNI。 | |
+| desc | 否 | 辅助 | 证书描述。 | certs for production env |
| labels | 否 | 匹配规则 | 标识附加属性的键值对。 | {"version":"v2","build":"16","env":"production"} |
| type | 否 | 辅助 | 标识证书的类型,默认值为 `server`。 | `client` 表示证书是客户端证书,APISIX 访问上游时使用;`server` 表示证书是服务端证书,APISIX 验证客户端请求时使用。 |
| status | 否 | 辅助 | 当设置为 `1` 时,启用此 SSL,默认值为 `1`。 | `1` 表示启用,`0` 表示禁用 |
@@ -1342,6 +1344,7 @@ Consumer Group 资源请求地址:/apisix/admin/consumer_groups/{id}
| 名称 | 必选项 | 类型 | 描述 | 示例值 |
|--------- |--------- |------|----------------------------------------------- |------|
|plugins | 是 |Plugin| 插件配置。详细信息请参考 [Plugin](terminology/plugin.md)。 | |
+|name | 否 | 辅助 | 消费者组名。 | premium-tier |
|desc | 否 | 辅助 | 标识描述、使用场景等。 | Consumer 测试。|
|labels | 否 | 辅助 | 标识附加属性的键值对。 |{"version":"v2","build":"16","env":"production"}|
@@ -1500,6 +1503,9 @@ Plugin 资源请求地址:/apisix/admin/stream_routes/{id}
| 名称 | 必选项 | 类型 | 描述 | 示例值 |
| ---------------- | ------| -------- | ------------------------------------------------------------------------------| ------ |
+| name | 否 | 辅助 | Stream 路由名。 | postgres-proxy |
+| desc | 否 | 辅助 | Stream 路由描述。 | proxy endpoint for postgresql |
+| labels | 否 | 匹配规则 | 标识附加属性的键值对。 | {"version":"17","service":"user","env":"production"} |
| upstream | 否 | Upstream | Upstream 配置,详细信息请参考 [Upstream](terminology/upstream.md)。 | |
| upstream_id | 否 | Upstream | 需要使用的 Upstream id,详细信息请 [Upstream](terminology/upstream.md)。 | |
| service_id | 否 | String | 需要使用的 [Service](terminology/service.md) id. | |
diff --git a/t/admin/metadata.spec.ts b/t/admin/metadata.spec.ts
new file mode 100644
index 000000000..39dfe1830
--- /dev/null
+++ b/t/admin/metadata.spec.ts
@@ -0,0 +1,170 @@
+/*
+ * 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.
+ */
+import { readFile } from 'node:fs/promises';
+import { resolve } from 'node:path';
+
+import { request as requestAdminAPI } from '../ts/admin_api';
+
+describe('Resource Metadata', () => {
+ describe('Consumer', () => {
+ it('should ensure additionalProperties is false', () =>
+ expect(
+ requestAdminAPI(
+ '/apisix/admin/consumers/jack',
+ 'PUT',
+ {
+ username: 'jack',
+ invalid: true,
+ },
+ undefined,
+ { validateStatus: () => true },
+ ),
+ ).resolves.toMatchObject({ status: 400 }));
+
+ it('should accept desc field', () =>
+ expect(
+ requestAdminAPI('/apisix/admin/consumers/jack', 'PUT', {
+ username: 'jack',
+ desc: 'test_desc',
+ }),
+ ).resolves.not.toThrow());
+ });
+
+ describe('Consumer Credentials', () => {
+ it('should ensure additionalProperties is false', () =>
+ expect(
+ requestAdminAPI(
+ '/apisix/admin/consumers/jack/credentials/cred1',
+ 'PUT',
+ {
+ plugins: { 'key-auth': { key: 'test' } },
+ invalid: true,
+ },
+ undefined,
+ { validateStatus: () => true },
+ ),
+ ).resolves.toMatchObject({ status: 400 }));
+
+ it('should accept name field', () =>
+ expect(
+ requestAdminAPI(
+ '/apisix/admin/consumers/jack/credentials/cred1',
+ 'PUT',
+ {
+ name: 'test_name',
+ plugins: { 'key-auth': { key: 'test' } },
+ },
+ ),
+ ).resolves.not.toThrow());
+ });
+
+ describe('SSL', () => {
+ const path = resolve(__dirname, '../certs/');
+ let cert: string;
+ let key: string;
+
+ beforeAll(async () => {
+ cert = await readFile(resolve(path, 'apisix.crt'), 'utf-8');
+ key = await readFile(resolve(path, 'apisix.key'), 'utf-8');
+ });
+
+ it('should ensure additionalProperties is false', () =>
+ expect(
+ requestAdminAPI(
+ '/apisix/admin/ssls/ssl1',
+ 'PUT',
+ { sni: 'test.com', cert, key, invalid: true },
+ undefined,
+ { validateStatus: () => true },
+ ),
+ ).resolves.toMatchObject({ status: 400 }));
+
+ it('should accept desc field', () =>
+ expect(
+ requestAdminAPI('/apisix/admin/ssls/ssl1', 'PUT', {
+ desc: 'test_desc',
+ sni: 'test.com',
+ cert,
+ key,
+ }),
+ ).resolves.not.toThrow());
+ });
+
+ describe('Proto', () => {
+ it('should ensure additionalProperties is false', () =>
+ expect(
+ requestAdminAPI(
+ '/apisix/admin/protos/proto1',
+ 'PUT',
+ { content: 'syntax = "proto3";', invalid: true },
+ undefined,
+ { validateStatus: () => true },
+ ),
+ ).resolves.toMatchObject({ status: 400 }));
+
+ it('should accept name/labels field', () =>
+ expect(
+ requestAdminAPI('/apisix/admin/protos/proto1', 'PUT', {
+ name: 'test_name',
+ labels: { test: 'test' },
+ content: 'syntax = "proto3";',
+ }),
+ ).resolves.not.toThrow());
+ });
+
+ describe('Stream Route', () => {
+ it('should ensure additionalProperties is false', () =>
+ expect(
+ requestAdminAPI(
+ '/apisix/admin/stream_routes/sr1',
+ 'PUT',
+ { upstream: { nodes: { '127.0.0.1:5432': 1 } }, invalid: true },
+ undefined,
+ { validateStatus: () => true },
+ ),
+ ).resolves.toMatchObject({ status: 400 }));
+
+ it('should accept name field', () =>
+ expect(
+ requestAdminAPI('/apisix/admin/stream_routes/sr1', 'PUT', {
+ name: 'test_name',
+ upstream: { nodes: { '127.0.0.1:5432': 1 } },
+ }),
+ ).resolves.not.toThrow());
+ });
+
+ describe('Consumer Group', () => {
+ it('should ensure additionalProperties is false', () =>
+ expect(
+ requestAdminAPI(
+ '/apisix/admin/consumer_groups/cg1',
+ 'PUT',
+ { plugins: {}, invalid: true },
+ undefined,
+ { validateStatus: () => true },
+ ),
+ ).resolves.toMatchObject({ status: 400 }));
+
+ it('should accept name field', () =>
+ expect(
+ requestAdminAPI('/apisix/admin/consumer_groups/cg1', 'PUT', {
+ name: 'test_name',
+ plugins: {},
+ }),
+ ).resolves.not.toThrow());
+ });
+});
diff --git a/t/admin/metadata.t b/t/admin/metadata.t
new file mode 100644
index 000000000..d27035e23
--- /dev/null
+++ b/t/admin/metadata.t
@@ -0,0 +1,36 @@
+#
+# 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();
+use_hup();
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: test
+--- timeout: 15
+--- max_size: 204800
+--- exec
+cd t && pnpm test admin/metadata.spec.ts 2>&1
+--- no_error_log
+failed to execute the script with status
+--- response_body eval
+qr/PASS admin\/metadata.spec.ts/
diff --git a/t/ts/admin_api.ts b/t/ts/admin_api.ts
index cccd91da9..6cc381330 100644
--- a/t/ts/admin_api.ts
+++ b/t/ts/admin_api.ts
@@ -14,22 +14,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import axios, { type Method, type RawAxiosRequestHeaders } from 'axios';
+import axios, {
+ type AxiosRequestConfig,
+ type Method,
+ type RawAxiosRequestHeaders,
+} from 'axios';
export const request = async (
- uri: string,
+ url: string,
method: Method = 'GET',
body?: object,
headers?: RawAxiosRequestHeaders,
+ config?: AxiosRequestConfig,
) => {
return axios.request({
method,
// TODO: use 9180 for admin api
- url: `http://127.0.0.1:1984/${uri}`,
+ baseURL: 'http://127.0.0.1:1984',
+ url,
data: body,
headers: {
'X-API-KEY': 'edd1c9f034335f136f87ad84b625c8f1',
...headers,
},
+ ...config,
});
};