diff --git a/src/dashboard-front/src/http/backends.ts b/src/dashboard-front/src/http/backends.ts index 11b36e2a9..6c14d8adb 100644 --- a/src/dashboard-front/src/http/backends.ts +++ b/src/dashboard-front/src/http/backends.ts @@ -1,10 +1,10 @@ import fetch from './fetch'; -// import { json2Query } from '@/common/util'; +import { json2Query } from '@/common/util'; const { BK_DASHBOARD_URL } = window; // 获取后端服务列表 -export const getBackendsListData = (apigwId: number) => fetch.get(`${BK_DASHBOARD_URL}/gateways/${apigwId}/backends/`); +export const getBackendsListData = (apigwId: number, data = {}) => fetch.get(`${BK_DASHBOARD_URL}/gateways/${apigwId}/backends/?${json2Query(data)}`); // 获取后端服务详情 export const getBackendsDetailData = (apigwId: number, backendId: number) => fetch.get(`${BK_DASHBOARD_URL}/gateways/${apigwId}/backends/${backendId}/`); diff --git a/src/dashboard-front/src/views/basic-info/common/type.ts b/src/dashboard-front/src/views/basic-info/common/type.ts index d80a47d91..03ac85952 100644 --- a/src/dashboard-front/src/views/basic-info/common/type.ts +++ b/src/dashboard-front/src/views/basic-info/common/type.ts @@ -15,7 +15,7 @@ export interface BasicInfoParams { created_time: string public_key: string is_official: boolean - publish_validate_msg: string + publish_validate_msg?: string } export interface DialogParams { diff --git a/src/dashboard-front/src/views/basic-info/index.vue b/src/dashboard-front/src/views/basic-info/index.vue index fd5122543..0b023c316 100644 --- a/src/dashboard-front/src/views/basic-info/index.vue +++ b/src/dashboard-front/src/views/basic-info/index.vue @@ -483,7 +483,10 @@ const handleOperate = async (type: string) => { if (['edit'].includes(type)) { basicInfoDetailData.value = _.cloneDeep(basicInfoData.value); - dialogEditData.value.isShow = true; + console.log('basicInfoDetailData.value', basicInfoDetailData.value); + setTimeout(() => { + dialogEditData.value.isShow = true; + }, 500); return; } diff --git a/src/dashboard-front/src/views/resource/setting/comps/back-config.vue b/src/dashboard-front/src/views/resource/setting/comps/back-config.vue index 2a8087b77..9d566c91c 100644 --- a/src/dashboard-front/src/views/resource/setting/comps/back-config.vue +++ b/src/dashboard-front/src/views/resource/setting/comps/back-config.vue @@ -9,8 +9,22 @@ - + + +
+ {{item.name}} + +
+
编辑服务 @@ -458,7 +472,7 @@ const handleMouseLeave = (e: Event, row: Record { - const res = await getBackendsListData(common.apigwId); + const res = await getBackendsListData(common.apigwId, { offset: 0, limit: 1000 }); servicesData.value = res.results; }; @@ -566,11 +580,26 @@ defineExpose({ top: -2px; font-size: 24px; cursor: pointer; - color: #3A84FF; + color: #3a84ff; } } } +.service-select-popover { + .service-select-item { + display: flex; + } + .desc { + color: #979ba5; + margin-left: 6px; + width: 560px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + display: inline-block; + } +} + :deep(.back-config-timeout) { display: inline-block; @@ -578,7 +607,7 @@ defineExpose({ .refresh-icon { margin-left: 8px; font-size: 16px; - color: #3A84FF; + color: #3a84ff; vertical-align: middle; cursor: pointer; } diff --git a/src/dashboard/apigateway/apigateway/apis/open/stage/serializers.py b/src/dashboard/apigateway/apigateway/apis/open/stage/serializers.py index 0633612e3..5ed636780 100644 --- a/src/dashboard/apigateway/apigateway/apis/open/stage/serializers.py +++ b/src/dashboard/apigateway/apigateway/apis/open/stage/serializers.py @@ -142,6 +142,21 @@ class StageProxyHTTPConfigSLZ(serializers.Serializer): transform_headers = TransformHeadersSLZ(required=False, default=dict) +class BackendConfigSLZ(UpstreamsSLZ): + timeout = serializers.IntegerField(max_value=MAX_BACKEND_TIMEOUT_IN_SECOND, min_value=1) + + class Meta: + ref_name = "apis.open.stage.BackendConfigSLZ" + + +class BackendSLZ(serializers.Serializer): + name = serializers.CharField(help_text="后端服务名称", required=True) + config = BackendConfigSLZ(allow_empty=False) + + class Meta: + ref_name = "apis.open.stage.BackendSLZ" + + class PluginConfigSLZ(serializers.Serializer): type = serializers.CharField(help_text="插件类型名称") yaml = serializers.CharField(help_text="插件yaml配置") @@ -158,7 +173,9 @@ class StageSLZ(ExtensibleFieldMixin, serializers.ModelSerializer): child=serializers.CharField(allow_blank=True, required=True), default=dict, ) - proxy_http = StageProxyHTTPConfigSLZ() + proxy_http = StageProxyHTTPConfigSLZ(required=False) + + backends = serializers.ListSerializer(help_text="后端配置", child=BackendSLZ(), allow_null=True, required=False) plugin_configs = serializers.ListSerializer( help_text="插件配置", child=PluginConfigSLZ(), allow_null=True, required=False @@ -181,6 +198,7 @@ class Meta: "vars", "status", "proxy_http", + "backends", "plugin_configs", "micro_gateway_id", ) @@ -190,7 +208,7 @@ class Meta: } } read_only_fields = ("id", "status") - non_model_fields = ["proxy_http", "plugin_configs", "rate_limit"] + non_model_fields = ["proxy_http", "backends", "plugin_configs", "rate_limit"] lookup_field = "id" validators = [ @@ -210,32 +228,63 @@ class Meta: def validate(self, data): self._validate_micro_gateway_stage_unique(data.get("micro_gateway_id")) self._validate_plugin_configs(data.get("plugin_configs")) + # validate stage backend + if data.get("proxy_http") is None and data.get("backends") is None: + raise serializers.ValidationError(_("proxy_http or backends 必须要选择一种方式配置后端服务")) return data def create(self, validated_data): # 1. save stage instance = super().create(validated_data) - proxy_http_config = validated_data["proxy_http"] - # 2. create default backend backend, _ = Backend.objects.get_or_create( gateway=instance.gateway, name=DEFAULT_BACKEND_NAME, ) - config = self._get_stage_backend_config(proxy_http_config) - backend_config = BackendConfig( - gateway=instance.gateway, - backend=backend, - stage=instance, - config=config, - ) - backend_config.save() + proxy_http_config = validated_data.get("proxy_http") + # 兼容老的配置 + if proxy_http_config is not None and len(proxy_http_config) != 0: + config = self._get_stage_backend_config(proxy_http_config) + backend_config = BackendConfig( + gateway=instance.gateway, + backend=backend, + stage=instance, + config=config, + ) + backend_config.save() + + # create or update header rewrite plugin config + stage_transform_headers = proxy_http_config.get("transform_headers") or {} + stage_config = HeaderRewriteConvertor.transform_headers_to_plugin_config(stage_transform_headers) + HeaderRewriteConvertor.sync_plugins( + instance.gateway_id, + PluginBindingScopeEnum.STAGE.value, + {instance.id: stage_config}, + self.context["request"].user.username, + ) - # 3. create other backend config with empty host - backends = Backend.objects.filter(gateway=instance.gateway).exclude(name=DEFAULT_BACKEND_NAME) + # 3.create config backend backend_configs = [] + names = [DEFAULT_BACKEND_NAME] + for backend_info in validated_data.get("backends", []): + names.append(backend_info["name"]) + backend, _ = Backend.objects.get_or_create( + gateway=instance.gateway, + name=backend_info["name"], + ) + config = self._get_stage_backend_config_v2(backend_info) + backend_config = BackendConfig( + gateway=instance.gateway, + backend=backend, + stage=instance, + config=config, + ) + backend_configs.append(backend_config) + + # 4. create other backend config with empty host + backends = Backend.objects.filter(gateway=instance.gateway).exclude(name__in=names) config = { "type": "node", "timeout": 30, @@ -255,16 +304,6 @@ def create(self, validated_data): if backend_configs: BackendConfig.objects.bulk_create(backend_configs) - # 4. create or update header rewrite plugin config - stage_transform_headers = proxy_http_config.get("transform_headers") or {} - stage_config = HeaderRewriteConvertor.transform_headers_to_plugin_config(stage_transform_headers) - HeaderRewriteConvertor.sync_plugins( - instance.gateway_id, - PluginBindingScopeEnum.STAGE.value, - {instance.id: stage_config}, - self.context["request"].user.username, - ) - # 5. sync stage plugin self._sync_plugins(instance.gateway_id, instance.id, validated_data.get("plugin_configs", None)) @@ -283,6 +322,19 @@ def _get_stage_backend_config(self, proxy_http_config): "hosts": hosts, } + def _get_stage_backend_config_v2(self, backend: dict): + hosts = [] + for host in backend["config"]["hosts"]: + scheme, _host = host["host"].rstrip("/").split("://") + hosts.append({"scheme": scheme, "host": _host, "weight": host["weight"]}) + + return { + "type": "node", + "timeout": backend["config"]["timeout"], + "loadbalance": backend["config"]["loadbalance"], + "hosts": hosts, + } + def update(self, instance, validated_data): validated_data.pop("name", None) # 仅能通过发布更新 status,不允许直接更新 status @@ -292,38 +344,58 @@ def update(self, instance, validated_data): # 1. 更新数据 instance = super().update(instance, validated_data) - proxy_http_config = validated_data["proxy_http"] - # 2. create default backend - backend, _ = Backend.objects.get_or_create( - gateway=instance.gateway, - name=DEFAULT_BACKEND_NAME, - ) - - backend_config = BackendConfig.objects.filter( - gateway=instance.gateway, - backend=backend, - stage=instance, - ).first() - if not backend_config: - backend_config = BackendConfig( + proxy_http_config = validated_data.get("proxy_http") + if proxy_http_config is not None and len(proxy_http_config) != 0: + backend, _ = Backend.objects.get_or_create( + gateway=instance.gateway, + name=DEFAULT_BACKEND_NAME, + ) + backend_config = BackendConfig.objects.filter( gateway=instance.gateway, backend=backend, stage=instance, + ).first() + if not backend_config: + backend_config = BackendConfig( + gateway=instance.gateway, + backend=backend, + stage=instance, + ) + + backend_config.config = self._get_stage_backend_config(proxy_http_config) + backend_config.save() + + # create or update header rewrite plugin config + stage_transform_headers = proxy_http_config.get("transform_headers") or {} + stage_config = HeaderRewriteConvertor.transform_headers_to_plugin_config(stage_transform_headers) + HeaderRewriteConvertor.sync_plugins( + instance.gateway_id, + PluginBindingScopeEnum.STAGE.value, + {instance.id: stage_config}, + self.context["request"].user.username, ) - backend_config.config = self._get_stage_backend_config(proxy_http_config) - backend_config.save() - - # 3. create or update header rewrite plugin config - stage_transform_headers = proxy_http_config.get("transform_headers") or {} - stage_config = HeaderRewriteConvertor.transform_headers_to_plugin_config(stage_transform_headers) - HeaderRewriteConvertor.sync_plugins( - instance.gateway_id, - PluginBindingScopeEnum.STAGE.value, - {instance.id: stage_config}, - self.context["request"].user.username, - ) + # 3. update backend + for backend_info in validated_data.get("backends", []): + backend, _ = Backend.objects.get_or_create( + gateway=instance.gateway, + name=backend_info["name"], + ) + backend_config = BackendConfig.objects.filter( + gateway=instance.gateway, + backend=backend, + stage=instance, + ).first() + + if not backend_config: + backend_config = BackendConfig( + gateway=instance.gateway, + backend=backend, + stage=instance, + ) + backend_config.config = self._get_stage_backend_config_v2(backend_info) + backend_config.save() # 4. sync stage plugin self._sync_plugins(instance.gateway_id, instance.id, validated_data.get("plugin_configs", None)) diff --git a/src/dashboard/apigateway/apigateway/apis/web/resource/serializers.py b/src/dashboard/apigateway/apigateway/apis/web/resource/serializers.py index 96a2188ab..199c2ec57 100644 --- a/src/dashboard/apigateway/apigateway/apis/web/resource/serializers.py +++ b/src/dashboard/apigateway/apigateway/apis/web/resource/serializers.py @@ -243,7 +243,7 @@ class Meta: validators = [ MaxCountPerGatewayValidator( Resource, - max_count_callback=lambda gateway: GatewayHandler.get_max_resource_count(gateway), + max_count_callback=lambda gateway: GatewayHandler.get_max_resource_count(gateway.name), message=gettext_lazy("每个网关最多创建 {max_count} 个资源。"), ), UniqueTogetherValidator( diff --git a/src/dashboard/apigateway/apigateway/apps/permission/tasks.py b/src/dashboard/apigateway/apigateway/apps/permission/tasks.py index cb18e9c19..16fec5054 100644 --- a/src/dashboard/apigateway/apigateway/apps/permission/tasks.py +++ b/src/dashboard/apigateway/apigateway/apps/permission/tasks.py @@ -76,7 +76,7 @@ def send_mail_for_perm_apply(record_id): "expire_days_display": PermissionApplyExpireDaysEnum.get_choice_label(record.expire_days), "grant_dimension_display": GrantDimensionEnum.get_choice_label(record.grant_dimension), "resource_names": sorted(manager.get_resource_names_display(record.gateway.id, record.resource_ids)), - "perm_apply_link": f"{apigw_domain}/{record.gateway_id}/permission/applys", + "perm_apply_link": f"{apigw_domain}/{record.gateway_id}/permission/apply", }, ) diff --git a/src/dashboard/apigateway/apigateway/biz/resource/resource.py b/src/dashboard/apigateway/apigateway/biz/resource/resource.py index ad344f120..c50df7d5b 100644 --- a/src/dashboard/apigateway/apigateway/biz/resource/resource.py +++ b/src/dashboard/apigateway/apigateway/biz/resource/resource.py @@ -150,10 +150,10 @@ def filter_by_resource_filter_condition(gateway_id: int, condition: Dict[str, An queryset = Resource.objects.filter(gateway_id=gateway_id) if condition.get("name"): - queryset = queryset.filter(name=condition["name"]) + queryset = queryset.filter(name__icontains=condition["name"]) if condition.get("path"): - queryset = queryset.filter(path=condition["path"]) + queryset = queryset.filter(path__icontains=condition["path"]) if condition.get("method"): queryset = queryset.filter(method__in=condition["method"].split(",")) diff --git a/src/dashboard/apigateway/apigateway/conf/default.py b/src/dashboard/apigateway/apigateway/conf/default.py index db56f6376..899eae3f1 100644 --- a/src/dashboard/apigateway/apigateway/conf/default.py +++ b/src/dashboard/apigateway/apigateway/conf/default.py @@ -361,7 +361,7 @@ REDIS_SENTINEL_ADDR_LIST = [tuple(addr.split(":")) for addr in REDIS_SENTINEL_ADDR_STR.split(",") if addr] # redis lock 配置 REDIS_PUBLISH_LOCK_TIMEOUT = env.int("BK_APIGW_PUBLISH_LOCK_TIMEOUT", 5) -REDIS_PUBLISH_LOCK_RETRY_GET_TIMES = env.int("BK_APIGW_PUBLISH_LOCK_RETRY_GET_TIMES", 1) +REDIS_PUBLISH_LOCK_RETRY_GET_TIMES = env.int("BK_APIGW_PUBLISH_LOCK_RETRY_GET_TIMES", 3) DEFAULT_REDIS_CONFIG = CHANNEL_REDIS_CONFIG = { "host": REDIS_HOST, diff --git a/src/dashboard/apigateway/apigateway/core/admin.py b/src/dashboard/apigateway/apigateway/core/admin.py index 75c844504..9450c0d3a 100644 --- a/src/dashboard/apigateway/apigateway/core/admin.py +++ b/src/dashboard/apigateway/apigateway/core/admin.py @@ -73,8 +73,8 @@ class StageResourceDisabledAdmin(admin.ModelAdmin): class ProxyAdmin(admin.ModelAdmin): - list_display = ["id", "type", "resource"] - search_fields = ["resource__id", "id"] + list_display = ["id", "type", "resource", "backend"] + search_fields = ["resource__id", "resource__name", "id"] class ResourceVersionAdmin(admin.ModelAdmin): @@ -146,13 +146,13 @@ class MicroGatewayReleaseHistoryAdmin(admin.ModelAdmin): class BackendAdmin(admin.ModelAdmin): list_display = ["id", "gateway", "type", "name", "description"] - search_fields = ["name"] + search_fields = ["name", "gateway__id", "gateway__name", "description"] list_filter = ["gateway"] class BackendConfigAdmin(admin.ModelAdmin): list_display = ["id", "gateway", "backend", "stage", "config"] - search_fields: List[str] = [] + search_fields: List[str] = ["backend__id"] list_filter = ["gateway", "backend", "stage"] diff --git a/src/dashboard/apigateway/apigateway/core/models.py b/src/dashboard/apigateway/apigateway/core/models.py index 2e9b3d588..95f34e06b 100644 --- a/src/dashboard/apigateway/apigateway/core/models.py +++ b/src/dashboard/apigateway/apigateway/core/models.py @@ -366,6 +366,9 @@ class Meta: unique_together = ("gateway", "name") db_table = "core_backend" + def __str__(self): + return f"" + class BackendConfig(TimestampedModelMixin, OperatorModelMixin): gateway = models.ForeignKey(Gateway, on_delete=models.PROTECT) diff --git a/src/dashboard/apigateway/apigateway/editions/ee/data/apigw-definitions/bk-apigateway-definition.yaml b/src/dashboard/apigateway/apigateway/editions/ee/data/apigw-definitions/bk-apigateway-definition.yaml index 635e5b0f8..1aeceea24 100644 --- a/src/dashboard/apigateway/apigateway/editions/ee/data/apigw-definitions/bk-apigateway-definition.yaml +++ b/src/dashboard/apigateway/apigateway/editions/ee/data/apigw-definitions/bk-apigateway-definition.yaml @@ -11,13 +11,21 @@ apigateway: stage: name: prod - vars: {} - proxy_http: - timeout: 60 - upstreams: - loadbalance: roundrobin - hosts: - - host: "{{ environ.DASHBOARD_INNER_URL }}" + backends: + - name: "default" + config: + timeout: 60 + loadbalance: "roundrobin" + hosts: + - host: "{{ environ.DASHBOARD_INNER_URL }}" + weight: 100 + - name: "core-api" + config: + timeout: 60 + loadbalance: "roundrobin" + hosts: + - host: "{{ environ.CORE_API_INNER_URL }}" + weight: 100 grant_permissions: - bk_app_code: visual-layout diff --git a/src/dashboard/apigateway/apigateway/editions/ee/data/apigw-definitions/bk-apigateway-resources.yaml b/src/dashboard/apigateway/apigateway/editions/ee/data/apigw-definitions/bk-apigateway-resources.yaml index 5e4bd9504..29f1c6180 100644 --- a/src/dashboard/apigateway/apigateway/editions/ee/data/apigw-definitions/bk-apigateway-resources.yaml +++ b/src/dashboard/apigateway/apigateway/editions/ee/data/apigw-definitions/bk-apigateway-resources.yaml @@ -119,9 +119,10 @@ paths: allowApplyPermission: true matchSubpath: false backend: + name: core-api type: HTTP method: get - path: /backend/api/v1/apis/{api_name}/public_key/ + path: /api/v1/open/gateways/{api_name}/public_key/ matchSubpath: false timeout: 0 upstreams: {} diff --git a/src/dashboard/apigateway/apigateway/fixtures/plugins.yaml b/src/dashboard/apigateway/apigateway/fixtures/plugins.yaml index 07b98e2c5..22ae5c91d 100644 --- a/src/dashboard/apigateway/apigateway/fixtures/plugins.yaml +++ b/src/dashboard/apigateway/apigateway/fixtures/plugins.yaml @@ -109,7 +109,7 @@ language: '' type: - bk-cors - notes: cors 插件可以让你轻松地为服务端启用 CORS(Cross-Origin Resource Sharing,跨域资源共享)的返回头。 + notes: cors 插件支持为后端服务增加 CORS(Cross-Origin Resource Sharing,跨域资源共享)的返回头。 style: dynamic default_value: '' config: |- @@ -164,7 +164,7 @@ ] }, "expose_headers": { - "description": "允许跨域访问时响应方携带哪些非 CORS 规范 以外的 Header。如果你有多个 Header,请使用 , 分割。当 allow_credential 为 false 时,可以使用 * 来表示允许任意 Header 。你也可以在启用了 allow_credential 后使用 ** 强制允许任意 Header,但请注意这样存在安全隐患。", + "description": "允许跨域访问时响应方携带哪些非 CORS 规范 以外的 Header。如果你有多个 Header,请使用 , 分割。当 allow_credential 为 false 时,可以使用 * 来表示允许任意 Header。你也可以在启用了 allow_credential 后使用 ** 强制允许任意 Header,但请注意这样存在安全隐患。", "type": "string", "pattern": "^(|\\*|\\*\\*|[-a-zA-Z0-9]+(,[-a-zA-Z0-9]+)*)$", "maxLength": 4096, @@ -340,7 +340,7 @@ language: '' type: - bk-header-rewrite - notes: Header 转换插件可以让你轻松地为后端服务的请求添加或删除请求头。 + notes: Header 转换插件支持对请求添加或删除请求头。 style: dynamic default_value: '' config: |- @@ -427,8 +427,7 @@ language: en type: - bk-header-rewrite - notes: Header transformation plugins allow you to easily add or remove request - headers for backend services. + notes: Header transformation plugins allow you to easily add or remove request headers. style: dynamic default_value: '' config: |- @@ -834,7 +833,9 @@ language: '' type: - bk-rate-limit - notes: 默认频率限制,表示单个应用的默认频率限制;特殊应用频率限制,对指定应用设置单独的频率限制。频率控制插件,绑定环境时,表示应用对环境下所有资源的总频率限制;绑定资源时,表示应用对单个资源的频率限制 + notes: |- + 默认频率限制,表示单个应用的默认频率限制;特殊应用频率限制,对指定应用设置单独的频率限制。频率控制插件,在环境上配置表示对应用访问环境下所有资源的总访问频率进行限制, + 在资源上配置,表示对应用访问单个资源的频率进行限制, 如果在环境和资源上都配置,两个都会生效,先校验资源的频率限制,再校验环境的频率限制。 style: dynamic default_value: '' config: |- diff --git a/src/dashboard/apigateway/apigateway/tests/apis/open/stage/test_views.py b/src/dashboard/apigateway/apigateway/tests/apis/open/stage/test_views.py index eb1959b67..f4dcbdfa7 100644 --- a/src/dashboard/apigateway/apigateway/tests/apis/open/stage/test_views.py +++ b/src/dashboard/apigateway/apigateway/tests/apis/open/stage/test_views.py @@ -22,9 +22,11 @@ from ddf import G from apigateway.apis.open.stage import views +from apigateway.biz.plugin_binding import PluginBindingHandler from apigateway.core.constants import StageStatusEnum -from apigateway.core.models import Gateway, Stage +from apigateway.core.models import Backend, BackendConfig, Gateway, Stage from apigateway.tests.utils.testing import get_response_json +from apigateway.utils.yaml import yaml_dumps pytestmark = pytest.mark.django_db @@ -185,3 +187,81 @@ def test_sync(self, mocker, unique_gateway_name, request_factory): stage = Stage.objects.get(gateway=gateway, name="prod") assert result["code"] == 0 assert stage.status == 0 + + def test_sync_backends(self, fake_plugin_type_bk_header_rewrite, mocker, unique_gateway_name, request_factory): + mocker.patch( + "apigateway.apis.open.stage.views.GatewayRelatedAppPermission.has_permission", + return_value=True, + ) + + mocker.patch( + "apigateway.common.plugin.header_rewrite.HeaderRewriteConvertor.sync_plugins", + return_value=True, + ) + + gateway = G(Gateway, name=unique_gateway_name, is_public=False) + + request = request_factory.post( + f"/api/v1/apis/{unique_gateway_name}/stages/sync/", + data={ + "name": "prod", + "description": "desc", + "vars": {}, + "backends": [ + { + "name": "default", + "config": { + "timeout": 60, + "loadbalance": "roundrobin", + "hosts": [ + { + "host": "http://www.a.com", + } + ], + }, + }, + { + "name": "service1", + "config": { + "timeout": 60, + "loadbalance": "roundrobin", + "hosts": [ + { + "host": "http://www.a.com", + } + ], + }, + }, + ], + "plugin_configs": [ + { + "type": "bk-header-rewrite", + "yaml": yaml_dumps( + { + "set": [{"key": "foo", "value": "bar"}], + "remove": [], + } + ), + } + ], + }, + ) + request.gateway = gateway + + view = views.StageSyncViewSet.as_view({"post": "sync"}) + response = view(request, gateway_name=unique_gateway_name) + + result = get_response_json(response) + stage = Stage.objects.get(gateway=gateway, name="prod") + + assert result["code"] == 0 + assert stage.status == 0 + assert len(Backend.objects.filter(gateway=gateway, name__in=["default", "service1"])) == 2 + assert len(BackendConfig.objects.filter(backend__name__in=["default", "service1"])) == 2 + assert BackendConfig.objects.get(backend__name="default").config == { + "type": "node", + "timeout": 60, + "loadbalance": "roundrobin", + "hosts": [{"scheme": "http", "host": "www.a.com", "weight": 100}], + } + assert len(PluginBindingHandler.get_stage_plugin_bindings(gateway.id, stage.id)) == 1 diff --git a/src/dashboard/apigateway/requirements.txt b/src/dashboard/apigateway/requirements.txt index 76503b108..91cf1a4bf 100644 --- a/src/dashboard/apigateway/requirements.txt +++ b/src/dashboard/apigateway/requirements.txt @@ -12,7 +12,7 @@ bk-notice-sdk==1.3.1 ; python_version >= "3.10" and python_version < "3.11" bkapi-bcs-api-gateway==1.12.1 ; python_version >= "3.10" and python_version < "3.11" bkapi-bk-apigateway==1.0.11 ; python_version >= "3.10" and python_version < "3.11" bkapi-client-core==1.2.0 ; python_version >= "3.10" and python_version < "3.11" -bkapi-client-generator==0.1.28 ; python_version >= "3.10" and python_version < "3.11" +bkapi-client-generator==0.1.29 ; python_version >= "3.10" and python_version < "3.11" bkapi-component-open==1.1.0 ; python_version >= "3.10" and python_version < "3.11" bkapi-paasv3==1.0.1 ; python_version >= "3.10" and python_version < "3.11" bkpaas-auth==2.0.6 ; python_version >= "3.10" and python_version < "3.11" diff --git a/src/dashboard/apigateway/requirements_dev.txt b/src/dashboard/apigateway/requirements_dev.txt index d85b0b56d..9180ff88e 100644 --- a/src/dashboard/apigateway/requirements_dev.txt +++ b/src/dashboard/apigateway/requirements_dev.txt @@ -13,7 +13,7 @@ bk-notice-sdk==1.3.1 ; python_version >= "3.10" and python_version < "3.11" bkapi-bcs-api-gateway==1.12.1 ; python_version >= "3.10" and python_version < "3.11" bkapi-bk-apigateway==1.0.11 ; python_version >= "3.10" and python_version < "3.11" bkapi-client-core==1.2.0 ; python_version >= "3.10" and python_version < "3.11" -bkapi-client-generator==0.1.28 ; python_version >= "3.10" and python_version < "3.11" +bkapi-client-generator==0.1.29 ; python_version >= "3.10" and python_version < "3.11" bkapi-component-open==1.1.0 ; python_version >= "3.10" and python_version < "3.11" bkapi-paasv3==1.0.1 ; python_version >= "3.10" and python_version < "3.11" bkpaas-auth==2.0.6 ; python_version >= "3.10" and python_version < "3.11" diff --git a/src/dashboard/poetry.lock b/src/dashboard/poetry.lock index c319686c5..94de6bec0 100644 --- a/src/dashboard/poetry.lock +++ b/src/dashboard/poetry.lock @@ -310,12 +310,12 @@ reference = "tencent" [[package]] name = "bkapi-client-generator" -version = "0.1.28" +version = "0.1.29" description = "" optional = false python-versions = ">=3.6,<4.0" files = [ - {file = "bkapi_client_generator-0.1.28-py3-none-any.whl", hash = "sha256:2bbd9ae20e79b0ef3e37baf50e37cc964d61b9c4f84b0d6dd87971b1d56cef58"}, + {file = "bkapi_client_generator-0.1.29-py3-none-any.whl", hash = "sha256:da1b1561cf05e0ed7b82e2a46f54feeb788557ec9fa3831aae133ca3c414e917"}, ] [package.source] @@ -4643,4 +4643,4 @@ reference = "tencent" [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.11" -content-hash = "3f06f6e4d38ca61002a0a5d73967dcc427960713e2ec3c2ade9cc94409d47626" +content-hash = "e9dae0e9150850a57fb89e0b2791a88b56744dc45ee313bea062f0b58d9fc251" diff --git a/src/dashboard/pyproject.toml b/src/dashboard/pyproject.toml index 8f48272b7..c52de7dcb 100644 --- a/src/dashboard/pyproject.toml +++ b/src/dashboard/pyproject.toml @@ -42,7 +42,7 @@ bk-crypto-python-sdk = "^1.0.4" bk-iam = "1.3.4" cryptography = "42.0.5" apigw-manager = "3.0.2" -bkapi-client-generator = "0.1.28" +bkapi-client-generator = "0.1.29" curlify = "^2.2.1" bkapi-component-open = "^1.0.3" django-prometheus = "2.2.0" diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 000000000..97a8002e8 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,3 @@ +cases/environments/local.bru +cases/data_1K.dat +cases/data_41M.dat diff --git a/test/README.md b/test/README.md new file mode 100644 index 000000000..61ef09175 --- /dev/null +++ b/test/README.md @@ -0,0 +1,18 @@ +test +============= + +存放测试相关的脚本/测试用例等 + +该目录在部署后,可以执行用以验证功能正确性,相当于集成测试 + +## run + +1. 准备好 `./cases/environments/local.bru` +2. 执行 `cd cases && ./run.sh` + +## 规范 + +- 默认:不勾选任何认证方式,并且不需要校验应用权限 +- 一个case只测试一项内容 +- 测试目录和文件名以`-`分隔,避免用空格 + diff --git a/test/cases/apisix/01-methods/delete.bru b/test/cases/apisix/01-methods/delete.bru new file mode 100644 index 000000000..d8043311a --- /dev/null +++ b/test/cases/apisix/01-methods/delete.bru @@ -0,0 +1,15 @@ +meta { + name: delete + type: http + seq: 5 +} + +delete { + url: {{scheme}}://{{host}}/api/httpbin/prod/delete + body: none + auth: none +} + +assert { + res.status: eq 200 +} diff --git a/test/cases/apisix/01-methods/get.bru b/test/cases/apisix/01-methods/get.bru new file mode 100644 index 000000000..44eff3401 --- /dev/null +++ b/test/cases/apisix/01-methods/get.bru @@ -0,0 +1,15 @@ +meta { + name: get + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/get + body: none + auth: none +} + +assert { + res.status: eq 200 +} diff --git a/test/cases/apisix/01-methods/missmatch.bru b/test/cases/apisix/01-methods/missmatch.bru new file mode 100644 index 000000000..9bcb57360 --- /dev/null +++ b/test/cases/apisix/01-methods/missmatch.bru @@ -0,0 +1,17 @@ +meta { + name: missmatch + type: http + seq: 6 +} + +delete { + url: {{scheme}}://{{host}}/api/httpbin/prod/put + body: none + auth: none +} + +assert { + res.status: eq 404 + res.body.code: eq 1640401 + res.body.code_name: eq API_NOT_FOUND +} diff --git a/test/cases/apisix/01-methods/patch.bru b/test/cases/apisix/01-methods/patch.bru new file mode 100644 index 000000000..fb05b7165 --- /dev/null +++ b/test/cases/apisix/01-methods/patch.bru @@ -0,0 +1,15 @@ +meta { + name: patch + type: http + seq: 4 +} + +patch { + url: {{scheme}}://{{host}}/api/httpbin/prod/patch + body: none + auth: none +} + +assert { + res.status: eq 200 +} diff --git a/test/cases/apisix/01-methods/post.bru b/test/cases/apisix/01-methods/post.bru new file mode 100644 index 000000000..eddfa0175 --- /dev/null +++ b/test/cases/apisix/01-methods/post.bru @@ -0,0 +1,15 @@ +meta { + name: post + type: http + seq: 2 +} + +post { + url: {{scheme}}://{{host}}/api/httpbin/prod/post + body: none + auth: none +} + +assert { + res.status: eq 200 +} diff --git a/test/cases/apisix/01-methods/put.bru b/test/cases/apisix/01-methods/put.bru new file mode 100644 index 000000000..5cd84b2c9 --- /dev/null +++ b/test/cases/apisix/01-methods/put.bru @@ -0,0 +1,15 @@ +meta { + name: put + type: http + seq: 3 +} + +put { + url: {{scheme}}://{{host}}/api/httpbin/prod/put + body: none + auth: none +} + +assert { + res.status: eq 200 +} diff --git a/test/cases/apisix/02-methods-mapping/get-to-post.bru b/test/cases/apisix/02-methods-mapping/get-to-post.bru new file mode 100644 index 000000000..0c2f3cfa7 --- /dev/null +++ b/test/cases/apisix/02-methods-mapping/get-to-post.bru @@ -0,0 +1,16 @@ +meta { + name: get-to-post + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/get_to_post + body: none + auth: none +} + +assert { + res.status: eq 200 + res.body.url: matches post +} diff --git a/test/cases/apisix/02-methods-mapping/post-to-get.bru b/test/cases/apisix/02-methods-mapping/post-to-get.bru new file mode 100644 index 000000000..7289618d3 --- /dev/null +++ b/test/cases/apisix/02-methods-mapping/post-to-get.bru @@ -0,0 +1,16 @@ +meta { + name: post-to-get + type: http + seq: 2 +} + +post { + url: {{scheme}}://{{host}}/api/smoke/prod/post_to_get + body: none + auth: none +} + +assert { + res.status: eq 200 + res.body.url: matches get +} diff --git a/test/cases/apisix/03-status/200.bru b/test/cases/apisix/03-status/200.bru new file mode 100644 index 000000000..f37f38362 --- /dev/null +++ b/test/cases/apisix/03-status/200.bru @@ -0,0 +1,15 @@ +meta { + name: 200 + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/status/200 + body: none + auth: none +} + +assert { + res.status: eq 200 +} diff --git a/test/cases/apisix/03-status/201.bru b/test/cases/apisix/03-status/201.bru new file mode 100644 index 000000000..71b89c06a --- /dev/null +++ b/test/cases/apisix/03-status/201.bru @@ -0,0 +1,15 @@ +meta { + name: 201 + type: http + seq: 2 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/status/201 + body: none + auth: none +} + +assert { + res.status: eq 201 +} diff --git a/test/cases/apisix/03-status/204.bru b/test/cases/apisix/03-status/204.bru new file mode 100644 index 000000000..5f6679681 --- /dev/null +++ b/test/cases/apisix/03-status/204.bru @@ -0,0 +1,15 @@ +meta { + name: 204 + type: http + seq: 3 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/status/204 + body: none + auth: none +} + +assert { + res.status: eq 204 +} diff --git a/test/cases/apisix/03-status/400.bru b/test/cases/apisix/03-status/400.bru new file mode 100644 index 000000000..d2c5bcd1b --- /dev/null +++ b/test/cases/apisix/03-status/400.bru @@ -0,0 +1,15 @@ +meta { + name: 400 + type: http + seq: 4 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/status/400 + body: none + auth: none +} + +assert { + res.status: eq 400 +} diff --git a/test/cases/apisix/03-status/401.bru b/test/cases/apisix/03-status/401.bru new file mode 100644 index 000000000..0bc5c15c5 --- /dev/null +++ b/test/cases/apisix/03-status/401.bru @@ -0,0 +1,15 @@ +meta { + name: 401 + type: http + seq: 5 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/status/401 + body: none + auth: none +} + +assert { + res.status: eq 401 +} diff --git a/test/cases/apisix/03-status/403.bru b/test/cases/apisix/03-status/403.bru new file mode 100644 index 000000000..b40fef70f --- /dev/null +++ b/test/cases/apisix/03-status/403.bru @@ -0,0 +1,15 @@ +meta { + name: 403 + type: http + seq: 6 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/status/403 + body: none + auth: none +} + +assert { + res.status: eq 403 +} diff --git a/test/cases/apisix/03-status/404.bru b/test/cases/apisix/03-status/404.bru new file mode 100644 index 000000000..7fb6d44dc --- /dev/null +++ b/test/cases/apisix/03-status/404.bru @@ -0,0 +1,15 @@ +meta { + name: 404 + type: http + seq: 7 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/status/404 + body: none + auth: none +} + +assert { + res.status: eq 404 +} diff --git a/test/cases/apisix/03-status/500.bru b/test/cases/apisix/03-status/500.bru new file mode 100644 index 000000000..3ff29ccbb --- /dev/null +++ b/test/cases/apisix/03-status/500.bru @@ -0,0 +1,15 @@ +meta { + name: 500 + type: http + seq: 8 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/status/500 + body: none + auth: none +} + +assert { + res.status: eq 500 +} diff --git a/test/cases/apisix/03-status/501.bru b/test/cases/apisix/03-status/501.bru new file mode 100644 index 000000000..144524d45 --- /dev/null +++ b/test/cases/apisix/03-status/501.bru @@ -0,0 +1,15 @@ +meta { + name: 501 + type: http + seq: 9 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/status/501 + body: none + auth: none +} + +assert { + res.status: eq 501 +} diff --git a/test/cases/apisix/03-status/502.bru b/test/cases/apisix/03-status/502.bru new file mode 100644 index 000000000..6f2fef374 --- /dev/null +++ b/test/cases/apisix/03-status/502.bru @@ -0,0 +1,15 @@ +meta { + name: 502 + type: http + seq: 10 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/status/502 + body: none + auth: none +} + +assert { + res.status: eq 502 +} diff --git a/test/cases/apisix/03-status/504.bru b/test/cases/apisix/03-status/504.bru new file mode 100644 index 000000000..51ced3ddd --- /dev/null +++ b/test/cases/apisix/03-status/504.bru @@ -0,0 +1,15 @@ +meta { + name: 504 + type: http + seq: 11 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/status/504 + body: none + auth: none +} + +assert { + res.status: eq 504 +} diff --git a/test/cases/apisix/04-content/form-url-encoded.bru b/test/cases/apisix/04-content/form-url-encoded.bru new file mode 100644 index 000000000..7d9e49fd9 --- /dev/null +++ b/test/cases/apisix/04-content/form-url-encoded.bru @@ -0,0 +1,31 @@ +meta { + name: form-url-encoded + type: http + seq: 4 +} + +post { + url: {{scheme}}://{{host}}/api/httpbin/prod/post + body: formUrlEncoded + auth: none +} + +body:json { + { + "foo": "bar" + } +} + +body:form-urlencoded { + foo: bar +} + +body:multipart-form { + foo: bar +} + +assert { + res.status: eq 200 + res.body.form.foo: eq bar + res.body.headers["Content-Type"]: eq application/x-www-form-urlencoded +} diff --git a/test/cases/apisix/04-content/get-query-string.bru b/test/cases/apisix/04-content/get-query-string.bru new file mode 100644 index 000000000..41b40f0ea --- /dev/null +++ b/test/cases/apisix/04-content/get-query-string.bru @@ -0,0 +1,22 @@ +meta { + name: get-query-string + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/get?a=1&foo=bar + body: none + auth: none +} + +query { + a: 1 + foo: bar +} + +assert { + res.status: eq 200 + res.body.args.a: eq "1" + res.body.args.foo: eq bar +} diff --git a/test/cases/apisix/04-content/headers.bru b/test/cases/apisix/04-content/headers.bru new file mode 100644 index 000000000..c87113b81 --- /dev/null +++ b/test/cases/apisix/04-content/headers.bru @@ -0,0 +1,27 @@ +meta { + name: headers + type: http + seq: 5 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/get?a=1&foo=bar + body: none + auth: none +} + +query { + a: 1 + foo: bar +} + +headers { + header-1: abc + Header-2: def +} + +assert { + res.status: eq 200 + res.body.headers["Header-1"]: eq abc + res.body.headers["Header-2"]: eq def +} diff --git a/test/cases/apisix/04-content/multipart-form.bru b/test/cases/apisix/04-content/multipart-form.bru new file mode 100644 index 000000000..2440425f9 --- /dev/null +++ b/test/cases/apisix/04-content/multipart-form.bru @@ -0,0 +1,27 @@ +meta { + name: multipart-form + type: http + seq: 3 +} + +post { + url: {{scheme}}://{{host}}/api/httpbin/prod/post + body: multipartForm + auth: none +} + +body:json { + { + "foo": "bar" + } +} + +body:multipart-form { + foo: bar +} + +assert { + res.status: eq 200 + res.body.form.foo: eq bar + res.body.headers["Content-Type"]: startsWith multipart/form-data; boundary= +} diff --git a/test/cases/apisix/04-content/post-json-body.bru b/test/cases/apisix/04-content/post-json-body.bru new file mode 100644 index 000000000..fd7a8c4af --- /dev/null +++ b/test/cases/apisix/04-content/post-json-body.bru @@ -0,0 +1,24 @@ +meta { + name: post-json-body + type: http + seq: 2 +} + +post { + url: {{scheme}}://{{host}}/api/httpbin/prod/post + body: json + auth: none +} + +body:json { + { + "foo": "bar" + } +} + +assert { + res.status: eq 200 + res.body.data: contains foo + res.body.json.foo: eq bar + res.body.headers["Content-Type"]: eq application/json +} diff --git a/test/cases/apisix/05-route/get-with-slash.bru b/test/cases/apisix/05-route/get-with-slash.bru new file mode 100644 index 000000000..042be8364 --- /dev/null +++ b/test/cases/apisix/05-route/get-with-slash.bru @@ -0,0 +1,15 @@ +meta { + name: get-with-slash + type: http + seq: 2 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/get/ + body: none + auth: none +} + +assert { + res.status: eq 200 +} diff --git a/test/cases/apisix/05-route/get-without-slash.bru b/test/cases/apisix/05-route/get-without-slash.bru new file mode 100644 index 000000000..8dace8062 --- /dev/null +++ b/test/cases/apisix/05-route/get-without-slash.bru @@ -0,0 +1,15 @@ +meta { + name: get-without-slash + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/get + body: none + auth: none +} + +assert { + res.status: eq 200 +} diff --git a/test/cases/apisix/05-route/not-found.bru b/test/cases/apisix/05-route/not-found.bru new file mode 100644 index 000000000..e83318a27 --- /dev/null +++ b/test/cases/apisix/05-route/not-found.bru @@ -0,0 +1,18 @@ +meta { + name: not-found + type: http + seq: 3 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/not_found + body: none + auth: none +} + +assert { + res.status: eq 404 + res.body.code: eq 1640401 + res.body.code_name: eq API_NOT_FOUND + res.body.message: matches API not found +} diff --git a/test/cases/apisix/06-backend/unknownbackend.bru b/test/cases/apisix/06-backend/unknownbackend.bru new file mode 100644 index 000000000..0c2e0fa18 --- /dev/null +++ b/test/cases/apisix/06-backend/unknownbackend.bru @@ -0,0 +1,18 @@ +meta { + name: unknownbackend + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/unknownbackend + body: none + auth: none +} + +assert { + res.status: eq 502 + res.body.code: eq 1650200 + res.body.code_name: eq BAD_GATEWAY + res.body.message: matches Bad Gateway +} diff --git a/test/cases/apisix/07-stage/stage-env.bru b/test/cases/apisix/07-stage/stage-env.bru new file mode 100644 index 000000000..d37cc03a0 --- /dev/null +++ b/test/cases/apisix/07-stage/stage-env.bru @@ -0,0 +1,15 @@ +meta { + name: stage-env + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/env_status_500 + body: none + auth: none +} + +assert { + res.status: eq 500 +} diff --git a/test/cases/apisix/08-plugins/000-bk-request-id/request_id-pre-setted.bru b/test/cases/apisix/08-plugins/000-bk-request-id/request_id-pre-setted.bru new file mode 100644 index 000000000..99f126b02 --- /dev/null +++ b/test/cases/apisix/08-plugins/000-bk-request-id/request_id-pre-setted.bru @@ -0,0 +1,21 @@ +meta { + name: request_id-pre-setted + type: http + seq: 2 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/get + body: none + auth: none +} + +headers { + X-Request-Id: foo +} + +assert { + res.status: eq 200 + res.headers["x-bkapi-request-id"]: length 36 + res.headers["x-request-id"]: eq foo +} diff --git a/test/cases/apisix/08-plugins/000-bk-request-id/request_id.bru b/test/cases/apisix/08-plugins/000-bk-request-id/request_id.bru new file mode 100644 index 000000000..59adc0ee3 --- /dev/null +++ b/test/cases/apisix/08-plugins/000-bk-request-id/request_id.bru @@ -0,0 +1,17 @@ +meta { + name: request_id + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/get + body: none + auth: none +} + +assert { + res.status: eq 200 + res.headers["x-bkapi-request-id"]: length 36 + res.headers["x-request-id"]: length 32 +} diff --git a/test/cases/apisix/08-plugins/001-bk-auth/app_verify-auth-in-body.bru b/test/cases/apisix/08-plugins/001-bk-auth/app_verify-auth-in-body.bru new file mode 100644 index 000000000..8a26e38c9 --- /dev/null +++ b/test/cases/apisix/08-plugins/001-bk-auth/app_verify-auth-in-body.bru @@ -0,0 +1,23 @@ +meta { + name: app_verify-auth-in-body + type: http + seq: 5 +} + +post { + url: {{scheme}}://{{host}}/api/smoke/prod/app_verify + body: json + auth: none +} + +body:json { + { + "bk_app_code": "{{bk_app_code}}", + "bk_app_secret": "{{bk_app_secret}}" + } +} + +assert { + res.status: eq 200 + res.body.headers["X-Bkapi-Jwt"]: isDefined +} diff --git a/test/cases/apisix/08-plugins/001-bk-auth/app_verify-auth-in-querystring.bru b/test/cases/apisix/08-plugins/001-bk-auth/app_verify-auth-in-querystring.bru new file mode 100644 index 000000000..e75e0c1d4 --- /dev/null +++ b/test/cases/apisix/08-plugins/001-bk-auth/app_verify-auth-in-querystring.bru @@ -0,0 +1,21 @@ +meta { + name: app_verify-auth-in-querystring + type: http + seq: 4 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/app_verify?bk_app_code={{bk_app_code}}&bk_app_secret={{bk_app_secret}} + body: none + auth: none +} + +query { + bk_app_code: {{bk_app_code}} + bk_app_secret: {{bk_app_secret}} +} + +assert { + res.status: eq 200 + res.body.headers["X-Bkapi-Jwt"]: isDefined +} diff --git a/test/cases/apisix/08-plugins/001-bk-auth/app_verify-auth-in-urlencoded.bru b/test/cases/apisix/08-plugins/001-bk-auth/app_verify-auth-in-urlencoded.bru new file mode 100644 index 000000000..545c75d18 --- /dev/null +++ b/test/cases/apisix/08-plugins/001-bk-auth/app_verify-auth-in-urlencoded.bru @@ -0,0 +1,21 @@ +meta { + name: app_verify-auth-in-urlencoded + type: http + seq: 6 +} + +post { + url: {{scheme}}://{{host}}/api/smoke/prod/app_verify + body: formUrlEncoded + auth: none +} + +body:form-urlencoded { + bk_app_code: {{bk_app_code}} + bk_app_secret: {{bk_app_secret}} +} + +assert { + res.status: eq 200 + res.body.headers["X-Bkapi-Jwt"]: isDefined +} diff --git a/test/cases/apisix/08-plugins/001-bk-auth/app_verify-invalid-bk_app_secret.bru b/test/cases/apisix/08-plugins/001-bk-auth/app_verify-invalid-bk_app_secret.bru new file mode 100644 index 000000000..901031608 --- /dev/null +++ b/test/cases/apisix/08-plugins/001-bk-auth/app_verify-invalid-bk_app_secret.bru @@ -0,0 +1,22 @@ +meta { + name: app_verify-invalid-bk_app_secret + type: http + seq: 3 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/app_verify + body: none + auth: none +} + +headers { + X-Bkapi-Authorization: {"bk_app_code": "{{bk_app_code}}", "bk_app_secret": "abc"} +} + +assert { + res.status: eq 400 + res.body.code: eq 1640001 + res.body.code_name: eq INVALID_ARGS + res.body.message: matches bk_app_code or bk_app_secret is incorrect +} diff --git a/test/cases/apisix/08-plugins/001-bk-auth/app_verify-no=auth-header.bru b/test/cases/apisix/08-plugins/001-bk-auth/app_verify-no=auth-header.bru new file mode 100644 index 000000000..dd6c3a407 --- /dev/null +++ b/test/cases/apisix/08-plugins/001-bk-auth/app_verify-no=auth-header.bru @@ -0,0 +1,18 @@ +meta { + name: app_verify-no=auth-header + type: http + seq: 2 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/app_verify + body: none + auth: none +} + +assert { + res.status: eq 400 + res.body.code: eq 1640001 + res.body.code_name: eq INVALID_ARGS + res.body.message: matches app code cannot be empty +} diff --git a/test/cases/apisix/08-plugins/001-bk-auth/app_verify-ok-auth-in-header.bru b/test/cases/apisix/08-plugins/001-bk-auth/app_verify-ok-auth-in-header.bru new file mode 100644 index 000000000..1f65c25df --- /dev/null +++ b/test/cases/apisix/08-plugins/001-bk-auth/app_verify-ok-auth-in-header.bru @@ -0,0 +1,20 @@ +meta { + name: app_verify-ok-auth-in-header + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/app_verify + body: none + auth: none +} + +headers { + X-Bkapi-Authorization: {"bk_app_code": "{{bk_app_code}}", "bk_app_secret": "{{bk_app_secret}}"} +} + +assert { + res.status: eq 200 + res.body.headers["X-Bkapi-Jwt"]: isDefined +} diff --git a/test/cases/apisix/08-plugins/001-bk-auth/user_verify-invalid-access_token.bru b/test/cases/apisix/08-plugins/001-bk-auth/user_verify-invalid-access_token.bru new file mode 100644 index 000000000..dd0d17ddf --- /dev/null +++ b/test/cases/apisix/08-plugins/001-bk-auth/user_verify-invalid-access_token.bru @@ -0,0 +1,21 @@ +meta { + name: user_verify-invalid-access_token + type: http + seq: 9 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/user_verify + body: none + auth: none +} + +headers { + X-Bkapi-Authorization: {"access_token": "abc"} +} + +assert { + res.status: eq 400 + res.body.code: eq 1640001 + res.body.code_name: eq INVALID_ARGS +} diff --git a/test/cases/apisix/08-plugins/001-bk-auth/user_verify-no-auth-header.bru b/test/cases/apisix/08-plugins/001-bk-auth/user_verify-no-auth-header.bru new file mode 100644 index 000000000..1e49f1570 --- /dev/null +++ b/test/cases/apisix/08-plugins/001-bk-auth/user_verify-no-auth-header.bru @@ -0,0 +1,18 @@ +meta { + name: user_verify-no-auth-header + type: http + seq: 8 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/user_verify + body: none + auth: none +} + +assert { + res.status: eq 400 + res.body.code: eq 1640001 + res.body.code_name: eq INVALID_ARGS + res.body.message: matches user authentication failed, please provide a valid user identity, such as bk_username, bk_token, access_token +} diff --git a/test/cases/apisix/08-plugins/001-bk-auth/user_verify-ok.bru b/test/cases/apisix/08-plugins/001-bk-auth/user_verify-ok.bru new file mode 100644 index 000000000..96ff1476b --- /dev/null +++ b/test/cases/apisix/08-plugins/001-bk-auth/user_verify-ok.bru @@ -0,0 +1,20 @@ +meta { + name: user_verify-ok + type: http + seq: 7 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/user_verify + body: none + auth: none +} + +headers { + X-Bkapi-Authorization: {"access_token": "{{access_token}}"} +} + +assert { + res.status: eq 200 + res.body.headers["X-Bkapi-Jwt"]: isDefined +} diff --git a/test/cases/apisix/08-plugins/002-bk-permission/no-permission.bru b/test/cases/apisix/08-plugins/002-bk-permission/no-permission.bru new file mode 100644 index 000000000..8c14cd776 --- /dev/null +++ b/test/cases/apisix/08-plugins/002-bk-permission/no-permission.bru @@ -0,0 +1,22 @@ +meta { + name: no-permission + type: http + seq: 2 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/app_verify + body: none + auth: none +} + +headers { + X-Bkapi-Authorization: {"bk_app_code": "{{test_bk_app_code}}", "bk_app_secret": "{{test_bk_app_secret}}"} +} + +assert { + res.status: eq 403 + res.body.code: eq 1640301 + res.body.code_name: eq APP_NO_PERMISSION + res.body.message: matches no permission +} diff --git a/test/cases/apisix/08-plugins/002-bk-permission/ok.bru b/test/cases/apisix/08-plugins/002-bk-permission/ok.bru new file mode 100644 index 000000000..65c8ecddb --- /dev/null +++ b/test/cases/apisix/08-plugins/002-bk-permission/ok.bru @@ -0,0 +1,20 @@ +meta { + name: ok + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/app_verify + body: none + auth: none +} + +headers { + X-Bkapi-Authorization: {"bk_app_code": "{{bk_app_code}}", "bk_app_secret": "{{bk_app_secret}}"} +} + +assert { + res.status: eq 200 + res.body.headers["X-Bkapi-Jwt"]: isDefined +} diff --git a/test/cases/apisix/08-plugins/003-bk-jwt/app-and-user-verified.bru b/test/cases/apisix/08-plugins/003-bk-jwt/app-and-user-verified.bru new file mode 100644 index 000000000..3590f918f --- /dev/null +++ b/test/cases/apisix/08-plugins/003-bk-jwt/app-and-user-verified.bru @@ -0,0 +1,45 @@ +meta { + name: app-and-user-verified + type: http + seq: 3 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/user_verify + body: none + auth: none +} + +headers { + X-Bkapi-Authorization: {"access_token": "{{access_token}}"} +} + +assert { + res.status: eq 200 + res.body.headers["X-Bkapi-Jwt"]: isString +} + +tests { + var atob = require('atob'); + function getJwtPayloadJsonData() { + const data = res.getBody(); + var jwtPayloadJson = atob(data.headers["X-Bkapi-Jwt"].split(".")[1]) + return JSON.parse(jwtPayloadJson); + } + + function getJwtHeaderJsonData() { + const data = res.getBody(); + var jwtHeaderJson = atob(data.headers["X-Bkapi-Jwt"].split(".")[0]) + return JSON.parse(jwtHeaderJson); + } + + test("check the payload", function() { + const data = res.getBody(); + var payload = getJwtPayloadJsonData(); + expect(payload.app.app_code).to.eql("bk_apigateway"); + expect(payload.app.verified).to.eql(true); + expect(payload.user.username).to.eql("admin"); + expect(payload.user.verified).to.eql(true); + }); + +} diff --git a/test/cases/apisix/08-plugins/003-bk-jwt/app-verified.bru b/test/cases/apisix/08-plugins/003-bk-jwt/app-verified.bru new file mode 100644 index 000000000..f2694b2a7 --- /dev/null +++ b/test/cases/apisix/08-plugins/003-bk-jwt/app-verified.bru @@ -0,0 +1,45 @@ +meta { + name: app-verified + type: http + seq: 2 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/app_verify + body: none + auth: none +} + +headers { + X-Bkapi-Authorization: {"bk_app_code": "{{bk_app_code}}", "bk_app_secret": "{{bk_app_secret}}"} +} + +assert { + res.status: eq 200 + res.body.headers["X-Bkapi-Jwt"]: isString +} + +tests { + var atob = require('atob'); + function getJwtPayloadJsonData() { + const data = res.getBody(); + var jwtPayloadJson = atob(data.headers["X-Bkapi-Jwt"].split(".")[1]) + return JSON.parse(jwtPayloadJson); + } + + function getJwtHeaderJsonData() { + const data = res.getBody(); + var jwtHeaderJson = atob(data.headers["X-Bkapi-Jwt"].split(".")[0]) + return JSON.parse(jwtHeaderJson); + } + + test("check the payload", function() { + const data = res.getBody(); + var payload = getJwtPayloadJsonData(); + expect(payload.app.app_code).to.eql("bk_apigateway"); + expect(payload.app.verified).to.eql(true); + expect(payload.user.username).to.eql(""); + expect(payload.user.verified).to.eql(false); + }); + +} diff --git a/test/cases/apisix/08-plugins/003-bk-jwt/not-verified.bru b/test/cases/apisix/08-plugins/003-bk-jwt/not-verified.bru new file mode 100644 index 000000000..a0b06cb93 --- /dev/null +++ b/test/cases/apisix/08-plugins/003-bk-jwt/not-verified.bru @@ -0,0 +1,45 @@ +meta { + name: not-verified + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/get + body: none + auth: none +} + +headers { + X-Bkapi-Authorization: {"bk_app_code": "x", "bk_username": "y"} +} + +assert { + res.status: eq 200 + res.body.headers["X-Bkapi-Jwt"]: isString 36 +} + +tests { + var atob = require('atob'); + function getJwtPayloadJsonData() { + const data = res.getBody(); + var jwtPayloadJson = atob(data.headers["X-Bkapi-Jwt"].split(".")[1]) + return JSON.parse(jwtPayloadJson); + } + + function getJwtHeaderJsonData() { + const data = res.getBody(); + var jwtHeaderJson = atob(data.headers["X-Bkapi-Jwt"].split(".")[0]) + return JSON.parse(jwtHeaderJson); + } + + test("check the payload", function() { + const data = res.getBody(); + var payload = getJwtPayloadJsonData(); + expect(payload.app.app_code).to.eql("x"); + expect(payload.app.verified).to.eql(false); + expect(payload.user.username).to.eql("y"); + expect(payload.user.verified).to.eql(false); + }); + +} diff --git a/test/cases/apisix/08-plugins/004-bk-break-recursive-call/different-instance.bru b/test/cases/apisix/08-plugins/004-bk-break-recursive-call/different-instance.bru new file mode 100644 index 000000000..c882e1037 --- /dev/null +++ b/test/cases/apisix/08-plugins/004-bk-break-recursive-call/different-instance.bru @@ -0,0 +1,23 @@ +meta { + name: different-instance + type: http + seq: 2 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/get?X-Bkapi-Instance-Id=abc + body: none + auth: none +} + +query { + X-Bkapi-Instance-Id: abc +} + +headers { + X-Bkapi-Instance-Id: abc +} + +assert { + res.status: eq 200 +} diff --git a/test/cases/apisix/08-plugins/004-bk-break-recursive-call/same-instance.bru b/test/cases/apisix/08-plugins/004-bk-break-recursive-call/same-instance.bru new file mode 100644 index 000000000..6dfcfb55e --- /dev/null +++ b/test/cases/apisix/08-plugins/004-bk-break-recursive-call/same-instance.bru @@ -0,0 +1,21 @@ +meta { + name: same-instance + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/get + body: none + auth: none +} + +headers { + X-Bkapi-Instance-Id: {{instance_id}} +} + +assert { + res.status: eq 508 + res.body.code: eq 1650801 + res.body.code_name: eq RECURSIVE_REQUEST_DETECTED +} diff --git a/test/cases/apisix/08-plugins/005-bk-delete-cookie/delete-cookie.bru b/test/cases/apisix/08-plugins/005-bk-delete-cookie/delete-cookie.bru new file mode 100644 index 000000000..788bea18a --- /dev/null +++ b/test/cases/apisix/08-plugins/005-bk-delete-cookie/delete-cookie.bru @@ -0,0 +1,20 @@ +meta { + name: delete-cookie + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/get + body: none + auth: none +} + +headers { + Cookie: a=b +} + +assert { + res.status: eq 200 + res.body.headers.Cookie: isUndefined a=b +} diff --git a/test/cases/apisix/08-plugins/006-bk-delete-sensitive/delete-from-form-url-encoded.bru b/test/cases/apisix/08-plugins/006-bk-delete-sensitive/delete-from-form-url-encoded.bru new file mode 100644 index 000000000..5dd74d9f8 --- /dev/null +++ b/test/cases/apisix/08-plugins/006-bk-delete-sensitive/delete-from-form-url-encoded.bru @@ -0,0 +1,60 @@ +meta { + name: delete-from-form-url-encoded + type: http + seq: 5 +} + +post { + url: {{scheme}}://{{host}}/api/httpbin/prod/post + body: formUrlEncoded + auth: none +} + +body:json { + { + "a": "b", + "access_token": "1", + "bk_app_code": "2", + "bk_app_secret": "3", + "app_secret": "4", + "bk_token": "5" + } +} + +body:form-urlencoded { + a: b + access_token: 1 + bk_app_code: 2 + bk_app_secret: 3 + app_secret: 4 + bk_token: 5 +} + +body:multipart-form { + a: b + access_token: 1 + bk_app_code: 2 + bk_app_secret: 3 + app_secret: 4 + bk_token: 5 +} + +assert { + res.status: eq 200 + res.body.form.a: eq b + res.body.form.bk_app_code: eq "2" + res.body.form.access_token: isUndefined + res.body.form.bk_app_secret: isUndefined + res.body.form.app_secret: isUndefined + res.body.form.bk_token: isUndefined +} + +docs { + 通过管理端新创建的网关,不需要删除敏感参数 allow_delete_sensitive_params=False + 通过 API 同步的,allow_delete_sensitive_params=True + + =False 的时候,不会检查并删除敏感参数 + + ----- + httpbin这个网关应该是自动注册的, allow_delete_sensitive_params=True +} diff --git a/test/cases/apisix/08-plugins/006-bk-delete-sensitive/delete-from-json-body.bru b/test/cases/apisix/08-plugins/006-bk-delete-sensitive/delete-from-json-body.bru new file mode 100644 index 000000000..6bac30874 --- /dev/null +++ b/test/cases/apisix/08-plugins/006-bk-delete-sensitive/delete-from-json-body.bru @@ -0,0 +1,42 @@ +meta { + name: delete-from-json-body + type: http + seq: 4 +} + +post { + url: {{scheme}}://{{host}}/api/httpbin/prod/post + body: json + auth: none +} + +body:json { + { + "a": "b", + "access_token": "1", + "bk_app_code": "2", + "bk_app_secret": "3", + "app_secret": "4", + "bk_token": "5" + } +} + +assert { + res.status: eq 200 + res.body.json.a: eq b + res.body.json.bk_app_code: eq "2" + res.body.json.access_token: isUndefined + res.body.json.bk_app_secret: isUndefined + res.body.json.app_secret: isUndefined + res.body.json.bk_token: isUndefined +} + +docs { + 通过管理端新创建的网关,不需要删除敏感参数 allow_delete_sensitive_params=False + 通过 API 同步的,allow_delete_sensitive_params=True + + =False 的时候,不会检查并删除敏感参数 + + ----- + httpbin这个网关应该是自动注册的, allow_delete_sensitive_params=True +} diff --git a/test/cases/apisix/08-plugins/006-bk-delete-sensitive/delete-from-querystring.bru b/test/cases/apisix/08-plugins/006-bk-delete-sensitive/delete-from-querystring.bru new file mode 100644 index 000000000..34b60757b --- /dev/null +++ b/test/cases/apisix/08-plugins/006-bk-delete-sensitive/delete-from-querystring.bru @@ -0,0 +1,40 @@ +meta { + name: delete-from-querystring + type: http + seq: 3 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/get?a=b&access_token=1&bk_app_code=2&bk_app_secret=3&app_secret=4&bk_token=5 + body: none + auth: none +} + +query { + a: b + access_token: 1 + bk_app_code: 2 + bk_app_secret: 3 + app_secret: 4 + bk_token: 5 +} + +assert { + res.status: eq 200 + res.body.args.a: eq b + res.body.args.bk_app_code: eq "2" + res.body.args.access_token: isUndefined + res.body.args.bk_app_secret: isUndefined + res.body.args.app_secret: isUndefined + res.body.args.bk_token: isUndefined +} + +docs { + 通过管理端新创建的网关,不需要删除敏感参数 allow_delete_sensitive_params=False + 通过 API 同步的,allow_delete_sensitive_params=True + + =False 的时候,不会检查并删除敏感参数 + + ----- + httpbin这个网关应该是自动注册的, allow_delete_sensitive_params=True +} diff --git a/test/cases/apisix/08-plugins/006-bk-delete-sensitive/delete-header-authorization.bru b/test/cases/apisix/08-plugins/006-bk-delete-sensitive/delete-header-authorization.bru new file mode 100644 index 000000000..151efab31 --- /dev/null +++ b/test/cases/apisix/08-plugins/006-bk-delete-sensitive/delete-header-authorization.bru @@ -0,0 +1,22 @@ +meta { + name: delete-header-authorization + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/get + body: none + auth: none +} + +headers { + a: b + X-Bkapi-Authorization: {"bk_app_code": "x", "bk_app_secret": "y"} +} + +assert { + res.status: eq 200 + res.body.headers.A: eq b + res.body.headers["X-Bkapi-Authorization"]: isUndefined +} diff --git a/test/cases/apisix/08-plugins/006-bk-delete-sensitive/delete-header-request-uri.bru b/test/cases/apisix/08-plugins/006-bk-delete-sensitive/delete-header-request-uri.bru new file mode 100644 index 000000000..3ead0df3d --- /dev/null +++ b/test/cases/apisix/08-plugins/006-bk-delete-sensitive/delete-header-request-uri.bru @@ -0,0 +1,22 @@ +meta { + name: delete-header-request-uri + type: http + seq: 2 +} + +get { + url: {{scheme}}://{{host}}/api/httpbin/prod/get + body: none + auth: none +} + +headers { + a: b + X-Request-Uri: abc +} + +assert { + res.status: eq 200 + res.body.headers.A: eq b + res.body.headers["X-Request-Uri"]: isUndefined +} diff --git a/test/cases/apisix/08-plugins/007-bk-status-rewrite/status-rewrite-500.bru b/test/cases/apisix/08-plugins/007-bk-status-rewrite/status-rewrite-500.bru new file mode 100644 index 000000000..cdfb46212 --- /dev/null +++ b/test/cases/apisix/08-plugins/007-bk-status-rewrite/status-rewrite-500.bru @@ -0,0 +1,22 @@ +meta { + name: status-rewrite-500 + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/plugin_status_rewrite + body: none + auth: none +} + +assert { + res.status: eq 200 + res.body.code: eq 1640001 + res.body.code_name: eq INVALID_ARGS + res.body.message: matches app code cannot be empty +} + +docs { + it rewrite 400 to 200 +} diff --git a/test/cases/apisix/08-plugins/008-bk-verified-user-exempted-apps/user-exempt-in-whitelist.bru b/test/cases/apisix/08-plugins/008-bk-verified-user-exempted-apps/user-exempt-in-whitelist.bru new file mode 100644 index 000000000..2fbc6e747 --- /dev/null +++ b/test/cases/apisix/08-plugins/008-bk-verified-user-exempted-apps/user-exempt-in-whitelist.bru @@ -0,0 +1,19 @@ +meta { + name: user-exempt-in-whitelist + type: http + seq: 2 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/app_and_user_verify + body: none + auth: none +} + +headers { + X-Bkapi-Authorization: {"bk_app_code": "{{test_bk_app_code}}", "bk_app_secret": "{{test_bk_app_secret}}", "bk_username": "admin"} +} + +assert { + res.status: eq 200 +} diff --git a/test/cases/apisix/08-plugins/008-bk-verified-user-exempted-apps/user-exempt.bru b/test/cases/apisix/08-plugins/008-bk-verified-user-exempted-apps/user-exempt.bru new file mode 100644 index 000000000..3b4514bfb --- /dev/null +++ b/test/cases/apisix/08-plugins/008-bk-verified-user-exempted-apps/user-exempt.bru @@ -0,0 +1,22 @@ +meta { + name: user-exempt + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/app_and_user_verify + body: none + auth: none +} + +headers { + X-Bkapi-Authorization: {"bk_app_code": "{{bk_app_code}}", "bk_app_secret": "{{bk_app_secret}}", "bk_username": "admin"} +} + +assert { + res.status: eq 400 + res.body.code: eq 1640001 + res.body.code_name: eq INVALID_ARGS + res.body.message: matches user authentication failed, the user indicated by bk_username is not verified +} diff --git a/test/cases/apisix/08-plugins/009-bk-cors/cors-not-hit.bru b/test/cases/apisix/08-plugins/009-bk-cors/cors-not-hit.bru new file mode 100644 index 000000000..a0ae1c6dc --- /dev/null +++ b/test/cases/apisix/08-plugins/009-bk-cors/cors-not-hit.bru @@ -0,0 +1,28 @@ +meta { + name: cors-not-hit + type: http + seq: 2 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/cors + body: none + auth: none +} + +headers { + Origin: http://abc.com +} + +assert { + res.status: eq 200 + res.body.headers["X-Bkapi-Jwt"]: isDefined + res.headers["access-control-allow-methods"]: isUndefined GET,POST,DELETE + res.headers["access-control-allow-headers"]: isUndefined x-request-id + res.headers["access-control-expose-headers"]: isUndefined x-bkapi-request-id + res.headers["access-control-max-age"]: isUndefined "3200" +} + +docs { + note here `access-control-allow-origin` and `access-control-allow-credentials` are from httpbin +} diff --git a/test/cases/apisix/08-plugins/009-bk-cors/cors-ok.bru b/test/cases/apisix/08-plugins/009-bk-cors/cors-ok.bru new file mode 100644 index 000000000..6b9d23b78 --- /dev/null +++ b/test/cases/apisix/08-plugins/009-bk-cors/cors-ok.bru @@ -0,0 +1,29 @@ +meta { + name: cors-ok + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/cors + body: none + auth: none +} + +headers { + Origin: http://example.com +} + +assert { + res.status: eq 200 + res.body.headers["X-Bkapi-Jwt"]: isDefined + res.headers["access-control-allow-origin"]: eq http://example.com + res.headers["access-control-allow-methods"]: eq GET,POST,DELETE + res.headers["access-control-allow-headers"]: eq x-request-id + res.headers["access-control-expose-headers"]: eq x-bkapi-request-id + res.headers["access-control-max-age"]: eq "3200" +} + +docs { + note here the access-control-allow-credentials:true is setted by httpbin (in case the `"allow_credential": false,` so it do nothing to the headers) +} diff --git a/test/cases/apisix/08-plugins/010-bk-ip-restriction/ip-blacklist-hit.bru b/test/cases/apisix/08-plugins/010-bk-ip-restriction/ip-blacklist-hit.bru new file mode 100644 index 000000000..565c2bf00 --- /dev/null +++ b/test/cases/apisix/08-plugins/010-bk-ip-restriction/ip-blacklist-hit.bru @@ -0,0 +1,21 @@ +meta { + name: ip-blacklist-hit + type: http + seq: 3 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/ip_blacklist + body: none + auth: none +} + +headers { + x-test-ip: 10.0.0.1 +} + +assert { + res.status: eq 403 + res.body.code: eq 1640302 + res.body.code_name: eq IP_NOT_ALLOWED +} diff --git a/test/cases/apisix/08-plugins/010-bk-ip-restriction/ip-blacklist-miss.bru b/test/cases/apisix/08-plugins/010-bk-ip-restriction/ip-blacklist-miss.bru new file mode 100644 index 000000000..82c9ae67b --- /dev/null +++ b/test/cases/apisix/08-plugins/010-bk-ip-restriction/ip-blacklist-miss.bru @@ -0,0 +1,15 @@ +meta { + name: ip-blacklist-miss + type: http + seq: 4 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/ip_blacklist + body: none + auth: none +} + +assert { + res.status: eq 200 +} diff --git a/test/cases/apisix/08-plugins/010-bk-ip-restriction/ip-whitelist-hit.bru b/test/cases/apisix/08-plugins/010-bk-ip-restriction/ip-whitelist-hit.bru new file mode 100644 index 000000000..d2dfe9399 --- /dev/null +++ b/test/cases/apisix/08-plugins/010-bk-ip-restriction/ip-whitelist-hit.bru @@ -0,0 +1,15 @@ +meta { + name: ip-whitelist-hit + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/ip_whitelist + body: none + auth: none +} + +headers { + X-test-IP: 10.0.0.1 +} diff --git a/test/cases/apisix/08-plugins/010-bk-ip-restriction/ip-whitelist-miss.bru b/test/cases/apisix/08-plugins/010-bk-ip-restriction/ip-whitelist-miss.bru new file mode 100644 index 000000000..abde4ab1b --- /dev/null +++ b/test/cases/apisix/08-plugins/010-bk-ip-restriction/ip-whitelist-miss.bru @@ -0,0 +1,17 @@ +meta { + name: ip-whitelist-miss + type: http + seq: 2 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/ip_whitelist + body: none + auth: none +} + +assert { + res.status: eq 403 + res.body.code: eq 1640302 + res.body.code_name: eq IP_NOT_ALLOWED +} diff --git a/test/cases/apisix/08-plugins/011-bk-stage-rate-limit/stage-rate-limit-1-limited.bru b/test/cases/apisix/08-plugins/011-bk-stage-rate-limit/stage-rate-limit-1-limited.bru new file mode 100644 index 000000000..96c79090c --- /dev/null +++ b/test/cases/apisix/08-plugins/011-bk-stage-rate-limit/stage-rate-limit-1-limited.bru @@ -0,0 +1,18 @@ +meta { + name: stage-rate-limit-1-limited + type: http + seq: 2 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/stage-rate-limit/get + body: none + auth: none +} + +assert { + res.status: eq 429 + res.body.code: eq 1642902 + res.body.code_name: eq RATE_LIMIT_RESTRICTION + res.body.message: eq API rate limit exceeded by stage strategy +} diff --git a/test/cases/apisix/08-plugins/011-bk-stage-rate-limit/stage-rate-limit-1-ok.bru b/test/cases/apisix/08-plugins/011-bk-stage-rate-limit/stage-rate-limit-1-ok.bru new file mode 100644 index 000000000..75f871b7b --- /dev/null +++ b/test/cases/apisix/08-plugins/011-bk-stage-rate-limit/stage-rate-limit-1-ok.bru @@ -0,0 +1,15 @@ +meta { + name: stage-rate-limit-1-ok + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/stage-rate-limit/get + body: none + auth: none +} + +assert { + res.status: eq 200 +} diff --git a/test/cases/apisix/08-plugins/012-bk-resource-rate-limit/resource-rate-limit-1-limited.bru b/test/cases/apisix/08-plugins/012-bk-resource-rate-limit/resource-rate-limit-1-limited.bru new file mode 100644 index 000000000..24037657d --- /dev/null +++ b/test/cases/apisix/08-plugins/012-bk-resource-rate-limit/resource-rate-limit-1-limited.bru @@ -0,0 +1,18 @@ +meta { + name: resource-rate-limit-1-limited + type: http + seq: 2 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/resource_rate_limit + body: none + auth: none +} + +assert { + res.status: eq 429 + res.body.code: eq 1642903 + res.body.code_name: eq RATE_LIMIT_RESTRICTION + res.body.message: eq API rate limit exceeded by resource strategy +} diff --git a/test/cases/apisix/08-plugins/012-bk-resource-rate-limit/resource-rate-limit-1-ok.bru b/test/cases/apisix/08-plugins/012-bk-resource-rate-limit/resource-rate-limit-1-ok.bru new file mode 100644 index 000000000..505f932fd --- /dev/null +++ b/test/cases/apisix/08-plugins/012-bk-resource-rate-limit/resource-rate-limit-1-ok.bru @@ -0,0 +1,15 @@ +meta { + name: resource-rate-limit-1-ok + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/resource_rate_limit + body: none + auth: none +} + +assert { + res.status: eq 200 +} diff --git a/test/cases/apisix/08-plugins/013-bk-stage-header-rewrite/stage-header-rewrite.bru b/test/cases/apisix/08-plugins/013-bk-stage-header-rewrite/stage-header-rewrite.bru new file mode 100644 index 000000000..6dfda4e28 --- /dev/null +++ b/test/cases/apisix/08-plugins/013-bk-stage-header-rewrite/stage-header-rewrite.bru @@ -0,0 +1,26 @@ +meta { + name: stage-header-rewrite + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/get + body: none + auth: none +} + +headers { + From-Env-B: world + From-Env-C: foo + from-env-override: bar +} + +assert { + res.status: eq 200 + res.body.headers["From-Env-A"]: eq hello + res.body.headers["From-Env-B"]: isUndefined + res.body.headers["From-Env-C"]: eq foo + res.body.headers["From-Env-Override"]: eq foo + res.body.headers["Priority"]: eq env +} diff --git a/test/cases/apisix/08-plugins/014-bk-resource-header-rewrite/resource-header-rewrite.bru b/test/cases/apisix/08-plugins/014-bk-resource-header-rewrite/resource-header-rewrite.bru new file mode 100644 index 000000000..6d246080d --- /dev/null +++ b/test/cases/apisix/08-plugins/014-bk-resource-header-rewrite/resource-header-rewrite.bru @@ -0,0 +1,26 @@ +meta { + name: resource-header-rewrite + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/resource_header_rewrite + body: none + auth: none +} + +headers { + From-Resource-B: world + From-Resource-C: foo + from-Resource-override: bar +} + +assert { + res.status: eq 200 + res.body.headers["From-Resource-A"]: eq hello + res.body.headers["From-Resource-B"]: isUndefined + res.body.headers["From-Resource-C"]: eq foo + res.body.headers["From-Resource-Override"]: eq foo + res.body.headers["Priority"]: eq resource +} diff --git a/test/cases/apisix/09-others/bodylimit-hit.bru b/test/cases/apisix/09-others/bodylimit-hit.bru new file mode 100644 index 000000000..70d6f8de1 --- /dev/null +++ b/test/cases/apisix/09-others/bodylimit-hit.bru @@ -0,0 +1,20 @@ +meta { + name: bodylimit-hit + type: http + seq: 2 +} + +post { + url: {{scheme}}://{{host}}/api/smoke/prod/post + body: multipartForm + auth: none +} + +body:multipart-form { + : @file(data_41M.dat) +} + +assert { + res.status: eq 413 + res.body: matches 413 Request Entity Too Large +} diff --git a/test/cases/apisix/09-others/bodylimit-not-hit.bru b/test/cases/apisix/09-others/bodylimit-not-hit.bru new file mode 100644 index 000000000..0f4914354 --- /dev/null +++ b/test/cases/apisix/09-others/bodylimit-not-hit.bru @@ -0,0 +1,21 @@ +meta { + name: bodylimit-not-hit + type: http + seq: 1 +} + +post { + url: {{scheme}}://{{host}}/api/smoke/prod/post + body: multipartForm + auth: none +} + +body:multipart-form { + : @file(data_1K.dat) +} + +assert { + res.status: eq 200 + res.body.headers["Content-Length"]: eq "1243" + res.body.files: isDefined +} diff --git a/test/cases/apisix/09-others/timeout.bru b/test/cases/apisix/09-others/timeout.bru new file mode 100644 index 000000000..c5b3e32dd --- /dev/null +++ b/test/cases/apisix/09-others/timeout.bru @@ -0,0 +1,18 @@ +meta { + name: timeout + type: http + seq: 3 +} + +get { + url: {{scheme}}://{{host}}/api/smoke/prod/timeout + body: none + auth: none +} + +assert { + res.status: eq 504 + res.body.code: eq 1650401 + res.body.code_name: eq REQUEST_BACKEND_TIMEOUT + res.body.message: matches cannot read header from upstream +} diff --git a/test/cases/apisix/apisix-healthz.bru b/test/cases/apisix/apisix-healthz.bru new file mode 100644 index 000000000..626387dad --- /dev/null +++ b/test/cases/apisix/apisix-healthz.bru @@ -0,0 +1,16 @@ +meta { + name: apisix-healthz + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/healthz + body: none + auth: none +} + +assert { + res.status: eq 200 + res.body: eq ok +} diff --git a/test/cases/bruno.json b/test/cases/bruno.json new file mode 100644 index 000000000..96cf587bd --- /dev/null +++ b/test/cases/bruno.json @@ -0,0 +1,9 @@ +{ + "version": "1", + "name": "blueking-apigateway", + "type": "collection", + "ignore": [ + "node_modules", + ".git" + ] +} \ No newline at end of file diff --git a/test/cases/dashboard-front/dashboard-fe.bru b/test/cases/dashboard-front/dashboard-fe.bru new file mode 100644 index 000000000..33738e51f --- /dev/null +++ b/test/cases/dashboard-front/dashboard-fe.bru @@ -0,0 +1,17 @@ +meta { + name: dashboard-fe + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{dashboard_host}}/ + body: none + auth: none +} + +assert { + res.status: eq 200 + res.headers["content-type"]: eq text/html + res.body: contains BK_APIGATEWAY_VERSION +} diff --git a/test/cases/dashboard/dashboard-healthz.bru b/test/cases/dashboard/dashboard-healthz.bru new file mode 100644 index 000000000..89e50342e --- /dev/null +++ b/test/cases/dashboard/dashboard-healthz.bru @@ -0,0 +1,17 @@ +meta { + name: dashboard-healthz + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{dashboard_host}}/backend/healthz + body: none + auth: none +} + +assert { + res.status: eq 200 + res.body: isJson + res.body.data: isNull +} diff --git a/test/cases/dashboard/open api/list-all-gateways.bru b/test/cases/dashboard/open api/list-all-gateways.bru new file mode 100644 index 000000000..6ca3be06e --- /dev/null +++ b/test/cases/dashboard/open api/list-all-gateways.bru @@ -0,0 +1,20 @@ +meta { + name: list-all-gateways + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/bk-apigateway/prod/api/v1/apis/ + body: none + auth: none +} + +headers { + X-Bkapi-Authorization: {"bk_app_code": "{{bk_app_code}}", "bk_app_secret": "{{bk_app_secret}}"} +} + +assert { + res.status: eq 200 + res.body.code: eq 0 +} diff --git a/test/cases/dashboard/open api/list-stages.bru b/test/cases/dashboard/open api/list-stages.bru new file mode 100644 index 000000000..a95d4deba --- /dev/null +++ b/test/cases/dashboard/open api/list-stages.bru @@ -0,0 +1,21 @@ +meta { + name: list-stages + type: http + seq: 2 +} + +get { + url: {{scheme}}://{{host}}/api/bk-apigateway/prod/api/v1/apis/bk-apigateway/stages/ + body: none + auth: none +} + +headers { + X-Bkapi-Authorization: {"bk_app_code": "{{bk_app_code}}", "bk_app_secret": "{{bk_app_secret}}"} +} + +assert { + res.status: eq 200 + res.body.code: eq 0 + res.body.data[0].name: eq prod +} diff --git a/test/cases/environments/dev.bru b/test/cases/environments/dev.bru new file mode 100644 index 000000000..2bfd12dc2 --- /dev/null +++ b/test/cases/environments/dev.bru @@ -0,0 +1,8 @@ +vars { + host: bkapi.example.com + scheme: http + instance_id: faf44a48-59e9-f790-2412-e56c90551fb3 + dashboard_host: apigw.example.com + bk_app_code: bk_apigateway + bk_app_secret: 358627d8-d3e8-4522-8f16-b5530776bbb8 +} diff --git a/test/cases/esb/esb-compapi-api.bru b/test/cases/esb/esb-compapi-api.bru new file mode 100644 index 000000000..229143251 --- /dev/null +++ b/test/cases/esb/esb-compapi-api.bru @@ -0,0 +1,21 @@ +meta { + name: esb-compapi-api + type: http + seq: 1 +} + +get { + url: {{scheme}}://{{host}}/api/c/compapi/v2/esb/get_systems/ + body: none + auth: none +} + +assert { + res.status: eq 200 + res.body.code: eq 1640001 + res.body.code_name: eq INVALID_ARGS +} + +docs { + not 404, means the bk-esb is working +} diff --git a/test/cases/esb/esb-gateway-api.bru b/test/cases/esb/esb-gateway-api.bru new file mode 100644 index 000000000..a906633b8 --- /dev/null +++ b/test/cases/esb/esb-gateway-api.bru @@ -0,0 +1,21 @@ +meta { + name: esb-gateway-api + type: http + seq: 2 +} + +get { + url: {{scheme}}://{{host}}/api/bk-esb/prod/v2/esb/get_systems/ + body: none + auth: none +} + +assert { + res.status: eq 200 + res.body.code: eq 1640001 + res.body.code_name: eq INVALID_ARGS +} + +docs { + not 404, means the bk-esb is working +} diff --git a/test/cases/run.sh b/test/cases/run.sh new file mode 100755 index 000000000..35f0ea003 --- /dev/null +++ b/test/cases/run.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# create data file +if [ ! -f data_1K.dat ] +then + dd if=/dev/zero of=data_1K.dat bs=1024 count=1 +fi + +if [ ! -f data_41M.dat ] +then + dd if=/dev/zero of=data_41M.dat bs=42991616 count=1 +fi + +# run cases +bru run . -r --env local +if [ $? -ne 0 ] +then + echo "run cases fail, please check" + exit 1 +else + echo "run cases success" + exit 0 +fi diff --git a/test/data/bk_apigw_resources_httpbin.yaml b/test/data/bk_apigw_resources_httpbin.yaml new file mode 100644 index 000000000..aeb798408 --- /dev/null +++ b/test/data/bk_apigw_resources_httpbin.yaml @@ -0,0 +1,1813 @@ +swagger: '2.0' +basePath: / +info: + version: '2.0' + title: API Gateway Resources + description: '' +schemes: +- http +paths: + /absolute-redirect/{n}: + get: + operationId: redirect_absolute + description: Absolutely 302 Redirects n times. + tags: + - Redirects + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /absolute-redirect/{n} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /anything: + delete: + operationId: anything_delete + description: Returns anything passed in request data. + tags: + - Anything + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: delete + path: /anything + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + get: + operationId: anything_get + description: Returns anything passed in request data. + tags: + - Anything + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /anything + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + patch: + operationId: anything_patch + description: Returns anything passed in request data. + tags: + - Anything + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: patch + path: /anything + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + post: + operationId: anything_post + description: Returns anything passed in request data. + tags: + - Anything + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: post + path: /anything + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + put: + operationId: anything_put + description: Returns anything passed in request data. + tags: + - Anything + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: put + path: /anything + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /anything/{anything}: + delete: + operationId: anything_anything_delete + description: Returns anything passed in request data. + tags: + - Anything + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: delete + path: /anything/{anything} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + get: + operationId: anything_anything_get + description: Returns anything passed in request data. + tags: + - Anything + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /anything/{anything} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + patch: + operationId: anything_anything_patch + description: Returns anything passed in request data. + tags: + - Anything + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: patch + path: /anything/{anything} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + post: + operationId: anything_anything_post + description: Returns anything passed in request data. + tags: + - Anything + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: post + path: /anything/{anything} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + put: + operationId: anything_anything_put + description: Returns anything passed in request data. + tags: + - Anything + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: put + path: /anything/{anything} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /base64/{value}: + get: + operationId: dynamic_data_base64 + description: Decodes base64url-encoded string. + tags: + - DynamicData + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /base64/{value} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /basic-auth/{user}/{passwd}: + get: + operationId: auth_basic + description: Prompts the user for authorization using HTTP Basic Auth. + tags: + - Auth + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /basic-auth/{user}/{passwd} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /bearer: + get: + operationId: auth_bearer + description: Prompts the user for authorization using bearer authentication. + tags: + - Auth + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /bearer + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /brotli: + get: + operationId: response_format_brotli + description: Returns Brotli-encoded data. + tags: + - ResponseFormats + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /brotli + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /bytes/{n}: + get: + operationId: dynamic_data_bytes_n + description: Returns n random bytes generated with given seed + tags: + - DynamicData + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /bytes/{n} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /cache: + get: + operationId: response_cache + description: Returns a 304 if an If-Modified-Since header or If-None-Match is present. Returns the same as a GET otherwise. + tags: + - ResponseInspection + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /cache + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /cache/{value}: + get: + operationId: response_cache_value + description: Sets a Cache-Control header for n seconds. + tags: + - ResponseInspection + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /cache/{value} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /cookies: + get: + operationId: cookies + description: Returns cookie data. + tags: + - Cookies + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /cookies + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /cookies/delete: + get: + operationId: cookies_delete + description: Deletes cookie(s) as provided by the query string and redirects to cookie list. + tags: + - Cookies + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /cookies/delete + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /cookies/set: + get: + operationId: cookies_set + description: Sets cookie(s) as provided by the query string and redirects to cookie list. + tags: + - Cookies + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /cookies/set + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /cookies/set/{name}/{value}: + get: + operationId: cookies_set_name_value + description: Sets a cookie and redirects to cookie list. + tags: + - Cookies + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /cookies/set/{name}/{value} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /deflate: + get: + operationId: response_format_deflate + description: Returns Deflate-encoded data. + tags: + - ResponseFormats + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /deflate + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /delay/{delay}: + delete: + operationId: dynamic_data_delay_delete + description: Returns a delayed response (max of 10 seconds). + tags: + - DynamicData + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: delete + path: /delay/{delay} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + get: + operationId: dynamic_data_delay_get + description: Returns a delayed response (max of 10 seconds). + tags: + - DynamicData + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /delay/{delay} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + patch: + operationId: dynamic_data_delay_patch + description: Returns a delayed response (max of 10 seconds). + tags: + - DynamicData + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: patch + path: /delay/{delay} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + post: + operationId: dynamic_data_delay_post + description: Returns a delayed response (max of 10 seconds). + tags: + - DynamicData + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: post + path: /delay/{delay} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + put: + operationId: dynamic_data_delay_put + description: Returns a delayed response (max of 10 seconds). + tags: + - DynamicData + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: put + path: /delay/{delay} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /delete: + delete: + operationId: delete + description: The request's DELETE parameters. + tags: + - HttpMethods + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: delete + path: /delete + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /deny: + get: + operationId: response_format_deny + description: Returns page denied by robots.txt rules. + tags: + - ResponseFormats + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /deny + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /digest-auth/{qop}/{user}/{passwd}: + get: + operationId: auth_digest + description: Prompts the user for authorization using Digest Auth. + tags: + - Auth + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /digest-auth/{qop}/{user}/{passwd} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /digest-auth/{qop}/{user}/{passwd}/{algorithm}: + get: + operationId: auth_digest_algorithm + description: Prompts the user for authorization using Digest Auth + Algorithm. + tags: + - Auth + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /digest-auth/{qop}/{user}/{passwd}/{algorithm} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /digest-auth/{qop}/{user}/{passwd}/{algorithm}/{stale_after}: + get: + operationId: auth_digest_algorithm_stale_after + description: Prompts the user for authorization using Digest Auth + Algorithm. + tags: + - Auth + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /digest-auth/{qop}/{user}/{passwd}/{algorithm}/{stale_after} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /drip: + get: + operationId: dynamic_data_drip + description: Drips data over a duration after an optional initial delay. + tags: + - DynamicData + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /drip + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /encoding/utf8: + get: + operationId: response_format_encoding_utf8 + description: Returns a UTF-8 encoded body. + tags: + - ResponseFormats + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /encoding/utf8 + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /etag/{etag}: + get: + operationId: response_etag + description: Assumes the resource has the given etag and responds to If-None-Match and If-Match headers appropriately. + tags: + - ResponseInspection + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /etag/{etag} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /get: + get: + operationId: get + description: The request's query parameters. + tags: + - HttpMethods + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /get + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /gzip: + get: + operationId: response_format_gzip + description: Returns GZip-encoded data. + tags: + - ResponseFormats + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /gzip + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /headers: + get: + operationId: request_headers + description: Return the incoming request's HTTP headers. + tags: + - RequestInspection + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /headers + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /hidden-basic-auth/{user}/{passwd}: + get: + operationId: auth_basic_hidden + description: Prompts the user for authorization using HTTP Basic Auth. + tags: + - Auth + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /hidden-basic-auth/{user}/{passwd} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /html: + get: + operationId: response_format_html + description: Returns a simple HTML document. + tags: + - ResponseFormats + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /html + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /image: + get: + operationId: image + description: Returns a simple image of the type suggest by the Accept header. + tags: + - Images + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /image + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /image/jpeg: + get: + operationId: image_jpeg + description: Returns a simple JPEG image. + tags: + - Images + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /image/jpeg + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /image/png: + get: + operationId: image_png + description: Returns a simple PNG image. + tags: + - Images + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /image/png + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /image/svg: + get: + operationId: image_svg + description: Returns a simple SVG image. + tags: + - Images + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /image/svg + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /image/webp: + get: + operationId: image_webp + description: Returns a simple WEBP image. + tags: + - Images + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /image/webp + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /ip: + get: + operationId: request_ip + description: Returns the requester's IP Address. + tags: + - RequestInspection + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /ip + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /json: + get: + operationId: response_format_json + description: Returns a simple JSON document. + tags: + - ResponseFormats + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /json + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /links/{n}/{offset}: + get: + operationId: dynamic_data_links + description: Generate a page containing n links to other pages which do the same. + tags: + - DynamicData + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /links/{n}/{offset} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /patch: + patch: + operationId: patch + description: The request's PATCH parameters. + tags: + - HttpMethods + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: patch + path: /patch + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /post: + post: + operationId: post + description: The request's POST parameters. + tags: + - HttpMethods + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: post + path: /post + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /put: + put: + operationId: put + description: The request's PUT parameters. + tags: + - HttpMethods + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: put + path: /put + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /range/{numbytes}: + get: + operationId: dynamic_data_range + description: Streams n random bytes generated with given seed, at given chunk size per packet. + tags: + - DynamicData + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /range/{numbytes} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /redirect-to: + delete: + operationId: redirect_delete + description: 302/3XX Redirects to the given URL. + tags: + - Redirects + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: delete + path: /redirect-to + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + get: + operationId: redirect_get + description: 302/3XX Redirects to the given URL. + tags: + - Redirects + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /redirect-to + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + patch: + operationId: redirect_patch + description: 302/3XX Redirects to the given URL. + tags: + - Redirects + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: patch + path: /redirect-to + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + post: + operationId: redirect_post + description: 302/3XX Redirects to the given URL. + tags: + - Redirects + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: post + path: /redirect-to + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + put: + operationId: redirect_put + description: 302/3XX Redirects to the given URL. + tags: + - Redirects + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: put + path: /redirect-to + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /redirect/{n}: + get: + operationId: redirect_n + description: 302 Redirects n times. + tags: + - Redirects + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /redirect/{n} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /relative-redirect/{n}: + get: + operationId: redirect_relative_n + description: Relatively 302 Redirects n times. + tags: + - Redirects + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /relative-redirect/{n} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /response-headers: + get: + operationId: response_headers_get + description: Returns a set of response headers from the query string. + tags: + - ResponseInspection + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /response-headers + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + post: + operationId: response_headers_post + description: Returns a set of response headers from the query string. + tags: + - ResponseInspection + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: post + path: /response-headers + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /robots.txt: + get: + operationId: response_format_robots_txt + description: Returns some robots.txt rules. + tags: + - ResponseFormats + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /robots.txt + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /status/{codes}: + delete: + operationId: status_delete + description: Return status code or random status code if more than one are given + tags: + - StatusCodes + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: delete + path: /status/{codes} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + get: + operationId: status_get + description: Return status code or random status code if more than one are given + tags: + - StatusCodes + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /status/{codes} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + patch: + operationId: status_patch + description: Return status code or random status code if more than one are given + tags: + - StatusCodes + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: patch + path: /status/{codes} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + post: + operationId: status_post + description: Return status code or random status code if more than one are given + tags: + - StatusCodes + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: post + path: /status/{codes} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + put: + operationId: status_put + description: Return status code or random status code if more than one are given + tags: + - StatusCodes + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: put + path: /status/{codes} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /stream-bytes/{n}: + get: + operationId: dynamic_data_stream_bytes + description: Streams n random bytes generated with given seed, at given chunk size per packet. + tags: + - DynamicData + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /stream-bytes/{n} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /stream/{n}: + get: + operationId: dynamic_data_stream + description: Stream n JSON responses + tags: + - DynamicData + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /stream/{n} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /user-agent: + get: + operationId: request_user_agent + description: Return the incoming requests's User-Agent header. + tags: + - RequestInspection + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /user-agent + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /uuid: + get: + operationId: dynamic_data_uuid + description: Return a UUID4. + tags: + - DynamicData + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /uuid + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /xml: + get: + operationId: response_format_xml + description: Returns a simple XML document. + tags: + - ResponseFormats + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /xml + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: diff --git a/test/data/bk_apigw_resources_smoke.yaml b/test/data/bk_apigw_resources_smoke.yaml new file mode 100644 index 000000000..8971ff401 --- /dev/null +++ b/test/data/bk_apigw_resources_smoke.yaml @@ -0,0 +1,448 @@ +swagger: '2.0' +basePath: / +info: + version: '2.0' + title: API Gateway Resources + description: '' +schemes: +- http +paths: + /app_verify: + get: + operationId: app_verify + description: '' + tags: [] + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /get + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: true + resourcePermissionRequired: true + descriptionEn: None + post: + operationId: app_verify_post + description: '' + tags: [] + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: true + allowApplyPermission: true + matchSubpath: false + backend: + name: default + method: get + path: /get + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: true + resourcePermissionRequired: true + descriptionEn: + /env_status_500: + get: + operationId: env_status_500 + description: '' + tags: [] + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: false + allowApplyPermission: false + matchSubpath: false + backend: + name: default + method: get + path: /status/{env.status_500} + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /get: + get: + operationId: get + description: '' + tags: [] + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: false + allowApplyPermission: false + matchSubpath: false + backend: + name: default + method: get + path: /get + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /get_to_post: + get: + operationId: get_to_post + description: '' + tags: [] + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: false + allowApplyPermission: false + matchSubpath: false + backend: + name: default + method: post + path: /post + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /post_to_get: + post: + operationId: post_to_get + description: '' + tags: [] + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: false + allowApplyPermission: false + matchSubpath: false + backend: + name: default + method: get + path: /get + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /timeout: + get: + operationId: timeout + description: '' + tags: [] + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: false + allowApplyPermission: false + matchSubpath: false + backend: + name: default + method: get + path: /delay/2 + matchSubpath: false + timeout: 1 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /unknownbackend: + get: + operationId: unknownbackend + description: '' + tags: [] + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: false + allowApplyPermission: false + matchSubpath: false + backend: + name: unknownbackend + method: get + path: /get + matchSubpath: false + timeout: 2 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /user_verify: + get: + operationId: user_verify + description: '' + tags: [] + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: false + allowApplyPermission: false + matchSubpath: false + backend: + name: default + method: get + path: /get + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: true + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /cors: + get: + operationId: cors + description: '' + tags: [] + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: false + allowApplyPermission: false + matchSubpath: false + backend: + name: default + method: get + path: /get + matchSubpath: false + timeout: 0 + pluginConfigs: + - type: bk-cors + yaml: |- + allow_origins: http://example.com,http://httpbin + allow_origins_by_regex: [] + allow_methods: GET,POST,DELETE + allow_headers: x-request-id + expose_headers: x-bkapi-request-id + max_age: 3200 + allow_credential: false + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /plugin_status_rewrite: + get: + operationId: plugin_status_rewrite + description: '' + tags: [] + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: false + allowApplyPermission: false + matchSubpath: false + backend: + name: default + method: get + path: /get + matchSubpath: false + timeout: 0 + pluginConfigs: + - type: bk-status-rewrite + yaml: '{}' + authConfig: + userVerifiedRequired: false + appVerifiedRequired: true + resourcePermissionRequired: false + descriptionEn: + /resource_header_rewrite: + get: + operationId: resource_header_rewrite + description: '' + tags: [] + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: false + allowApplyPermission: false + matchSubpath: false + backend: + name: default + method: get + path: /get + matchSubpath: false + timeout: 0 + pluginConfigs: + - type: bk-header-rewrite + yaml: |- + set: + - key: from-resource-a + value: hello + - key: from-resource-override + value: foo + - key: priority + value: resource + remove: + - key: from-resource-b + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /resource_rate_limit: + get: + operationId: resource_rate_limit + description: '' + tags: [] + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: false + allowApplyPermission: false + matchSubpath: false + backend: + name: default + method: get + path: /get + matchSubpath: false + timeout: 0 + pluginConfigs: + - type: bk-rate-limit + yaml: | + rates: + __default: + - period: 1 + tokens: 1 + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /ip_whitelist: + get: + operationId: ip_whitelist + description: '' + tags: [] + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: false + allowApplyPermission: false + matchSubpath: false + backend: + name: default + method: get + path: /get + matchSubpath: false + timeout: 0 + pluginConfigs: + - type: bk-ip-restriction + yaml: 'whitelist: 10.0.0.1' + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /ip_blacklist: + get: + operationId: ip_blacklist + description: '' + tags: [] + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: false + allowApplyPermission: false + matchSubpath: false + backend: + name: default + method: get + path: /get + matchSubpath: false + timeout: 0 + pluginConfigs: + - type: bk-ip-restriction + yaml: 'blacklist: 10.0.0.1' + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: + /app_and_user_verify: + get: + operationId: app_and_user_verify + description: '' + tags: [] + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: false + allowApplyPermission: false + matchSubpath: false + backend: + name: default + method: get + path: /get + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: true + appVerifiedRequired: true + resourcePermissionRequired: false + descriptionEn: + /post: + post: + operationId: post + description: '' + tags: [] + responses: + default: + description: '' + x-bk-apigateway-resource: + isPublic: false + allowApplyPermission: false + matchSubpath: false + backend: + name: default + method: post + path: /post + matchSubpath: false + timeout: 0 + pluginConfigs: [] + authConfig: + userVerifiedRequired: false + appVerifiedRequired: false + resourcePermissionRequired: false + descriptionEn: