Feat: add tip and preset model for plugin editor, improve e2e stability (#2581)
diff --git a/web/cypress/e2e/plugin/create-delete-in-drawer-plugin.cy.js b/web/cypress/e2e/plugin/create-delete-in-drawer-plugin.cy.js
index 4a2b6cf..f665600 100644
--- a/web/cypress/e2e/plugin/create-delete-in-drawer-plugin.cy.js
+++ b/web/cypress/e2e/plugin/create-delete-in-drawer-plugin.cy.js
@@ -292,12 +292,12 @@
cy.contains(item)
.parents(selector.pluginCardBordered)
.within(() => {
- cy.get('button').click();
+ cy.get('button').click({ force: true });
});
cy.get(selector.drawer)
.should('be.visible')
.within(() => {
- cy.get(selector.checkedSwitcher).should('exist');
+ cy.get(selector.disabledSwitcher).should('exist');
});
cy.wait(timeout);
cy.contains('button', 'Delete').click({ force: true });
diff --git a/web/cypress/e2e/plugin/create-route-with-plugin-orchestration.cy.js b/web/cypress/e2e/plugin/create-route-with-plugin-orchestration.cy.js
index 4c3801d..e297b86 100644
--- a/web/cypress/e2e/plugin/create-route-with-plugin-orchestration.cy.js
+++ b/web/cypress/e2e/plugin/create-route-with-plugin-orchestration.cy.js
@@ -59,6 +59,7 @@
cy.contains('Next').click();
cy.get(selector.groupButton).contains('Orchestration').click();
+ cy.wait(1000);
cy.get(selector.canvas).should('be.visible');
// Plugin Orchestration
diff --git a/web/cypress/e2e/plugin/plugin-schema.cy.js b/web/cypress/e2e/plugin/plugin-schema.cy.js
index ed46957..37929ad 100644
--- a/web/cypress/e2e/plugin/plugin-schema.cy.js
+++ b/web/cypress/e2e/plugin/plugin-schema.cy.js
@@ -75,10 +75,6 @@
} else {
cy.log(`${name} not a global plugin, skipping`);
}
-
- if (cases.length === i + 1) {
- cy.reload(true);
- }
});
});
});
diff --git a/web/cypress/e2e/rest/service-edit-service-with-upstream.cy.js b/web/cypress/e2e/rest/service-edit-service-with-upstream.cy.js
index c3f6981..8badbbb 100644
--- a/web/cypress/e2e/rest/service-edit-service-with-upstream.cy.js
+++ b/web/cypress/e2e/rest/service-edit-service-with-upstream.cy.js
@@ -88,7 +88,9 @@
cy.contains('Search').click();
cy.contains(data.serviceName).siblings().contains('Configure').click();
+ cy.contains(data.upstreamName).click();
cy.wait(500);
+ cy.contains('.ant-select-item-option-content','Custom').click();
cy.get(selector.nodes_0_host)
.click({
force: true,
@@ -96,8 +98,8 @@
.should('value', data.ip1);
cy.get(selector.input).should('be.disabled');
+ cy.wait(500);
cy.get(selector.upstreamSelector).click();
- cy.contains('.ant-select-item-option-content', 'Custom').click();
cy.get(selector.nodes_0_host).should('not.be.disabled').clear().type(data.ip2);
cy.get(selector.nodes_0_port).type(data.port);
cy.get(selector.nodes_0_weight).type(data.weight);
diff --git a/web/cypress/e2e/rest/upstream-create_and_edit_upstream_with_no_nodes.cy.js b/web/cypress/e2e/rest/upstream-create_and_edit_upstream_with_no_nodes.cy.js
index fb57def..ce15556 100644
--- a/web/cypress/e2e/rest/upstream-create_and_edit_upstream_with_no_nodes.cy.js
+++ b/web/cypress/e2e/rest/upstream-create_and_edit_upstream_with_no_nodes.cy.js
@@ -64,7 +64,6 @@
cy.get(selector.upstreamNodeMinus0).should('not.exist');
cy.contains('button', 'Next').should('not.be.disabled').click();
- cy.contains('button', 'Next').should('not.be.disabled').click();
cy.contains('Submit').click({
force: true,
});
diff --git a/web/package.json b/web/package.json
index 727b62e..925782c 100644
--- a/web/package.json
+++ b/web/package.json
@@ -6,7 +6,7 @@
"scripts": {
"prepare": "cd .. && husky install web/.husky",
"analyze": "cross-env ANALYZE=1 yarn run build",
- "build": "cp -R ./node_modules/monaco-editor ./public/ && umi build",
+ "build": "cp -R ./node_modules/monaco-editor ./public/ && NODE_OPTIONS=--max_old_space_size=4096 umi build",
"dev": "yarn run start:dev",
"fetch:blocks": "pro fetch-blocks --branch antd@4 && yarn run prettier",
"i18n-remove": "pro i18n-remove --locale=zh-CN --write",
diff --git a/web/src/components/Plugin/Models.ts b/web/src/components/Plugin/Models.ts
new file mode 100644
index 0000000..2e2e550
--- /dev/null
+++ b/web/src/components/Plugin/Models.ts
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Model List of PluginType of Monaco editor
+ */
+
+import { Uri, editor } from "monaco-editor";
+import * as modelCode from './modelCode'
+
+/**
+* Model type is authentication as fllows:
+*/
+export const authzcasbinModel = editor.createModel(
+ modelCode.authzcasbin,
+ "json",
+ Uri.parse("file:authz-casbin")
+ );
+
+export const authzkeycloakModel = editor.createModel(
+ modelCode.authzkeycloak,
+ "json",
+ Uri.parse("file:authz-keycloak")
+);
+
+export const forwardauthModel = editor.createModel(
+ modelCode.forwardauth,
+ "json",
+ Uri.parse("file:forward-auth")
+);
+
+export const opaModel = editor.createModel(
+ modelCode.opa,
+ "json",
+ Uri.parse("file:opa")
+);
+
+export const openidconnectModel = editor.createModel(
+ modelCode.openidconnect,
+ "json",
+ Uri.parse("file:openid-connect")
+);
+
+/**
+* Model type is security as fllows:
+*/
+
+export const csrfModel = editor.createModel(
+ modelCode.csrf,
+ "json",
+ Uri.parse("file:csrf")
+);
+
+export const iprestrictionModel = editor.createModel(
+ modelCode.iprestriction,
+ "json",
+ Uri.parse("file:ip-restriction")
+);
+
+export const uarestrictionModel = editor.createModel(
+ modelCode.uarestriction,
+ "json",
+ Uri.parse("file:ua-restriction")
+);
+
+export const uriblockerModel = editor.createModel(
+ modelCode.uriblocker,
+ "json",
+ Uri.parse("file:uri-blocker")
+);
+
+/**
+* Model type is traffic as fllows:
+*/
+
+export const clientcontrolModel = editor.createModel(
+ modelCode.clientcontrol,
+ "json",
+ Uri.parse("file:client-control")
+);
+
+export const trafficsplitModel = editor.createModel(
+ modelCode.trafficsplit,
+ "json",
+ Uri.parse("file:traffic-split")
+);
+
+/**
+* Model type is serverless as fllows:
+*/
+
+export const awslambdaModel = editor.createModel(
+ modelCode.awslambda,
+ "json",
+ Uri.parse("file:aws-lambda")
+);
+
+export const azurefunctionsModel = editor.createModel(
+ modelCode.azurefunctions,
+ "json",
+ Uri.parse("file:azure-functions")
+);
+
+export const openwhiskModel = editor.createModel(
+ modelCode.openwhisk,
+ "json",
+ Uri.parse("file:openwhisk")
+);
+
+/**
+* Model type is observability as fllows:
+*/
+
+export const clickhouseloggerModel = editor.createModel(
+ modelCode.clickhouselogger,
+ "json",
+ Uri.parse("file:clickhouse-logger")
+);
+
+export const fileloggerModel = editor.createModel(
+ modelCode.filelogger,
+ "json",
+ Uri.parse("file:file-logger")
+);
+
+export const googlecloudloggingModel = editor.createModel(
+ modelCode.googlecloudlogging,
+ "json",
+ Uri.parse("file:google-cloud-logging")
+);
+
+export const httploggerModel = editor.createModel(
+ modelCode.httplogger,
+ "json",
+ Uri.parse("file:http-logger")
+);
+
+export const kafkaloggerModel = editor.createModel(
+ modelCode.kafkalogger,
+ "json",
+ Uri.parse("file:kafka-logger")
+);
+
+export const logglyModel = editor.createModel(
+ modelCode.loggly,
+ "json",
+ Uri.parse("file:loggly")
+);
+
+export const rocketmqloggerModel = editor.createModel(
+ modelCode.rocketmqlogger,
+ "json",
+ Uri.parse("file:rocketmq-logger")
+);
+
+export const skywalkingModel = editor.createModel(
+ modelCode.skywalking,
+ "json",
+ Uri.parse("file:sky-walking")
+);
+
+export const slsloggerModel = editor.createModel(
+ modelCode.slslogger,
+ "json",
+ Uri.parse("file:sls-logger")
+);
+
+export const splunkhecloggingModel = editor.createModel(
+ modelCode.splunkheclogging,
+ "json",
+ Uri.parse("file:splunk-hec-logging")
+);
+
+export const syslogModel = editor.createModel(
+ modelCode.syslog,
+ "json",
+ Uri.parse("file:syslog")
+);
+
+export const tcploggerModel = editor.createModel(
+ modelCode.tcplogger,
+ "json",
+ Uri.parse("file:tcp-logger")
+);
+
+export const zipkinModel = editor.createModel(
+ modelCode.zipkin,
+ "json",
+ Uri.parse("file:zipkin")
+);
+
+/**
+* Model type is other as fllows:
+*/
+
+export const extpluginprereqModel = editor.createModel(
+ modelCode.extpluginprereq,
+ "json",
+ Uri.parse("file:ext-plugin-pre-req")
+);
+
+export const realipModel = editor.createModel(
+ modelCode.realip,
+ "json",
+ Uri.parse("file:real-ip")
+);
diff --git a/web/src/components/Plugin/PluginDetail.tsx b/web/src/components/Plugin/PluginDetail.tsx
index 4170a15..c4b3996 100644
--- a/web/src/components/Plugin/PluginDetail.tsx
+++ b/web/src/components/Plugin/PluginDetail.tsx
@@ -43,6 +43,8 @@
import { fetchSchema } from './service';
import { json2yaml, yaml2json } from '../../helpers';
import { PluginForm, PLUGIN_UI_LIST } from './UI';
+import * as allModels from './Models';
+import * as modelCode from './modelCode';
type Props = {
name: string;
@@ -116,6 +118,9 @@
{ label: monacoModeList.YAML, value: monacoModeList.YAML },
];
const targetPluginName = pluginList.find((item) => item.name === name)?.name;
+ const filteredName = name.replace("-","");
+ const targetModel = allModels[`${filteredName}Model`];
+ const targetModelCode = modelCode?.[`${filteredName}`];
if (PLUGIN_UI_LIST.includes(name)) {
modeOptions.push({
@@ -477,12 +482,13 @@
}
}}
language={monacoMode.toLocaleLowerCase()}
+ beforeMount={editorWillMount}
onMount={(editor) => {
// NOTE: for debug & test
// @ts-ignore
window.monacoEditor = editor;
+ if(targetModel)editor.setValue(targetModelCode);
}}
- beforeMount={editorWillMount}
options={{
scrollbar: {
vertical: 'hidden',
diff --git a/web/src/components/Plugin/modelCode.ts b/web/src/components/Plugin/modelCode.ts
new file mode 100644
index 0000000..52f04ba
--- /dev/null
+++ b/web/src/components/Plugin/modelCode.ts
@@ -0,0 +1,316 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Model Code List of Models of Monaco editor
+ */
+
+/**
+* Model code of authentication type as fllows:
+*/
+
+export const authzcasbin = `{
+ "model_path":
+ "policy_path":
+ "username":
+}`;
+
+export const authzkeycloak =`{
+ "token_endpoint":
+ "permissions":
+ "audience":
+}
+`;
+
+export const forwardauth =`{
+ "uri":
+ "request_headers":
+ "upstream_headers":
+ "client_headers":
+}
+`;
+
+export const opa = `{
+ "type":
+ "request": {
+ "scheme":
+ "path":
+ "headers": {
+ "user-agent":
+ "accept":
+ "host":
+ },
+ "query":
+ "port":
+ "method":
+ "host":
+ },
+ "var": {
+ "timestamp":
+ "server_addr":
+ "server_port":
+ "remote_port":
+ "remote_addr":
+ },
+ "route":
+ "service":
+ "consumer":
+}
+`;
+
+export const openidconnect =`{
+ "client_id":
+ "client_secret":
+ "discovery":
+ "introspection_endpoint":
+ "bearer_only":
+ "realm":
+ "introspection_endpoint_auth_method":
+}
+`;
+
+/**
+* Model code of security type as fllows:
+*/
+
+export const csrf =`{
+ "key":
+}
+`;
+
+export const iprestriction =`{
+ "whitelist":
+}
+`;
+
+export const uarestriction =`{
+ "bypass_missing":
+ "allowlist":
+ "denylist":
+}
+`;
+
+export const uriblocker =`{
+ "block_rules":
+}
+`;
+
+/**
+* Model code of traffic type as fllows:
+*/
+
+export const clientcontrol =`{
+ "max_body_size":
+}
+`;
+
+export const trafficsplit =`{
+ "rules": [
+ {
+ "weighted_upstreams": [
+ {
+ "upstream": {
+ "name":
+ "type":
+ "nodes":
+ "timeout": {
+ "connect":
+ "send":
+ "read":
+ }
+ },
+ "weight":
+ },
+ {
+ "weight":
+ }
+ ]
+ }
+ ]
+}
+`;
+
+/**
+* Model code of serverless type as fllows:
+*/
+
+export const awslambda =`{
+ "token_endpoint":
+ "permissions":
+ "audience":
+}
+`;
+
+export const azurefunctions =`{
+ "azure-functions": {
+ "function_uri":
+ "authorization": {
+ "apikey":
+ }
+ }
+}
+`;
+
+export const openwhisk =`{
+ "api_host":
+ "service_token":
+ "namespace":
+ "action":
+}
+`;
+
+/**
+* Model code of observability type as fllows:
+*/
+
+export const clickhouselogger =`{
+ "user":
+ "password":
+ "database":
+ "logtable":
+ "endpoint_addr":
+}
+`;
+
+export const filelogger =`{
+ "path":
+}
+`;
+
+export const googlecloudlogging =`{
+ "auth_config":{
+ "project_id":
+ "private_key":
+ "token_uri":
+ "scopes":
+ "entries_uri":
+ },
+ "resource":{
+ "type":
+ },
+ "log_id":
+ "inactive_timeout":
+ "max_retry_count":
+ "buffer_duration":
+ "retry_delay":
+ "batch_max_size":
+}
+`;
+
+export const httplogger =`{
+ "uri":
+}
+`;
+
+export const kafkalogger =`{
+ "broker_list" :
+ "kafka_topic" :
+ "key" :
+ "batch_max_size":
+ "name":
+}
+`;
+
+export const loggly =`{
+ "nameserver_list":
+ "topic":
+ "batch_max_size":
+ "name":
+}
+`;
+
+export const rocketmqlogger =`{
+ "token_endpoint":
+ "permissions":
+ "audience":
+}
+`;
+
+export const skywalking =`{
+ "sample_ratio":
+}
+`;
+
+export const skywalkinglogger =`{
+ "endpoint_addr":
+}
+`;
+
+export const slslogger =`{
+ "host":
+ "port":
+ "project":
+ "logstore":
+ "access_key_id":
+ "access_key_secret":
+ "timeout":
+}
+`;
+
+export const splunkheclogging =`{
+ "endpoint":{
+ "uri":
+ "token":
+ "channel":
+ "timeout":
+ },
+ "buffer_duration":
+ "max_retry_count":
+ "retry_delay":
+ "inactive_timeout":
+ "batch_max_size":
+}
+`;
+
+export const syslog =`{
+ "host":
+ "port":
+ "flush_limit":
+}
+`;
+
+export const tcplogger =`{
+ "host":
+ "port":
+ "tls":
+ "batch_max_size":
+ "name":
+}
+`;
+
+export const zipkin =`{
+ "endpoint":
+ "sample_ratio":
+ "service_name":
+ "server_addr":
+}
+`;
+
+/**
+* Model code of other type as fllows:
+*/
+
+export const extpluginprereq =`{
+ "conf":
+}
+`;
+
+export const realip =`{
+ "source":
+ "trusted_addresses":
+}
+`;
diff --git a/web/src/locales/en-US/component.ts b/web/src/locales/en-US/component.ts
index fb11721..a9e6643 100644
--- a/web/src/locales/en-US/component.ts
+++ b/web/src/locales/en-US/component.ts
@@ -26,6 +26,7 @@
'component.global.enable': 'Enable',
'component.global.disable': 'Disable',
'component.global.scope': 'Scope',
+ 'component.global.example': 'Example',
'component.global.data.editor': 'Raw Data Editor',
'component.global.delete': 'Delete',
'component.global.cancel': 'Cancel',
diff --git a/web/src/locales/tr-TR/component.ts b/web/src/locales/tr-TR/component.ts
index 031355c..58c8b16 100644
--- a/web/src/locales/tr-TR/component.ts
+++ b/web/src/locales/tr-TR/component.ts
@@ -26,6 +26,7 @@
'component.global.enable': 'Aktifleştir',
'component.global.disable': 'Devre dışı bırak',
'component.global.scope': 'Kapsam',
+ 'component.global.example': 'Örnek',
'component.global.data.editor': 'Veri Editörü',
'component.global.delete': 'Sil',
'component.global.cancel': 'İptal',
diff --git a/web/src/locales/zh-CN/component.ts b/web/src/locales/zh-CN/component.ts
index a7902e0..bf48733 100644
--- a/web/src/locales/zh-CN/component.ts
+++ b/web/src/locales/zh-CN/component.ts
@@ -26,6 +26,7 @@
'component.global.enable': '启用',
'component.global.disable': '禁用',
'component.global.scope': '作用域',
+ 'component.global.example': '例子',
'component.global.data.editor': '数据编辑器',
'component.global.delete': '删除',
'component.global.cancel': '取消',