You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampark.apache.org by be...@apache.org on 2023/02/28 13:59:27 UTC
[incubator-streampark] branch dev updated: [Feature][Issue-2191][Issue-2215] Support external link #2331 (#2375)
This is an automated email from the ASF dual-hosted git repository.
benjobs pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/incubator-streampark.git
The following commit(s) were added to refs/heads/dev by this push:
new 38eb26ca6 [Feature][Issue-2191][Issue-2215] Support external link #2331 (#2375)
38eb26ca6 is described below
commit 38eb26ca67f9828fefd47f3eb7231e8136f5f068
Author: Leomax_Sun <28...@qq.com>
AuthorDate: Tue Feb 28 21:59:19 2023 +0800
[Feature][Issue-2191][Issue-2215] Support external link #2331 (#2375)
* [Feature] Backend implementation for flink external link (#2191/#2215)
* [Feature] Frontend add external link (#2191)
* [Feature] Frontend render badge in details page(#2215)
* [Improve] Frontend add external link permission control(#2191/#2215)
* [Improve] Frontend support badges customization(#2191/#2215)
* fix: change id to optional
---------
Co-authored-by: asuiah <li...@gmail.com>
---
.../src/main/assembly/script/data/mysql-data.sql | 10 ++
.../src/main/assembly/script/data/pgsql-data.sql | 8 +
.../main/assembly/script/schema/mysql-schema.sql | 13 ++
.../main/assembly/script/schema/pgsql-schema.sql | 21 +++
.../main/assembly/script/upgrade/mysql/2.1.0.sql | 31 ++++
.../main/assembly/script/upgrade/pgsql/2.1.0.sql | 31 ++++
.../core/controller/ExternalLinkController.java | 110 ++++++++++++
.../console/core/entity/ExternalLink.java | 54 ++++++
.../console/core/enums/PlaceholderType.java} | 25 ++-
.../console/core/mapper/ExternalLinkMapper.java} | 14 +-
.../console/core/service/ExternalLinkService.java} | 21 ++-
.../core/service/impl/ExternalLinkServiceImpl.java | 119 +++++++++++++
.../src/main/resources/db/data-h2.sql | 9 +
.../src/main/resources/db/schema-h2.sql | 15 ++
.../resources/mapper/core/ExternalLinkMapper.xml | 30 ++++
.../src/api/flink/setting/externalLink.ts | 74 ++++++++
.../api/flink/setting/types/externalLink.type.ts} | 26 ++-
.../src/locales/lang/en/flink/setting.ts | 25 +++
.../src/locales/lang/zh-CN/flink/setting.ts | 25 +++
.../src/views/flink/app/Detail.vue | 36 +++-
.../src/views/flink/setting/View.vue | 12 ++
.../flink/setting/components/ExternalLinkBadge.vue | 61 +++++++
.../flink/setting/components/ExternalLinkModal.vue | 196 +++++++++++++++++++++
.../setting/components/ExternalLinkSetting.vue | 128 ++++++++++++++
.../views/flink/setting/hooks/useExternalLink.tsx | 65 +++++++
25 files changed, 1113 insertions(+), 46 deletions(-)
diff --git a/streampark-console/streampark-console-service/src/main/assembly/script/data/mysql-data.sql b/streampark-console/streampark-console-service/src/main/assembly/script/data/mysql-data.sql
index 67f5d1b77..a013079cd 100644
--- a/streampark-console/streampark-console-service/src/main/assembly/script/data/mysql-data.sql
+++ b/streampark-console/streampark-console-service/src/main/assembly/script/data/mysql-data.sql
@@ -118,6 +118,11 @@ insert into `t_menu` values (100067, 100053, 'view', NULL, NULL, 'variable:view'
insert into `t_menu` values (100068, 100033, 'view', null, null, 'setting:view', null, '1', 1, null, now(), now());
insert into `t_menu` values (100069, 100053, 'depend view', null, null, 'variable:depend_apps', null, '1', 1, NULL, now(), now());
insert into `t_menu` values (100070, 100015, 'savepoint trigger', null, null, 'savepoint:trigger', null, '1', 1, null, now(), now());
+insert into `t_menu` values (100071, 100033, 'link view', null, null, 'externalLink:view', null, '1', 1, NULL, now(), now());
+insert into `t_menu` values (100072, 100033, 'link create', null, null, 'externalLink:create', null, '1', 1, NULL, now(), now());
+insert into `t_menu` values (100073, 100033, 'link update', null, null, 'externalLink:update', null, '1', 1, NULL, now(), now());
+insert into `t_menu` values (100074, 100033, 'link delete', null, null, 'externalLink:delete', null, '1', 1, NULL, now(), now());
+
-- ----------------------------
-- Records of t_role
@@ -188,6 +193,11 @@ insert into `t_role_menu` values (100057, 100001, 100069);
insert into `t_role_menu` values (100058, 100002, 100057);
insert into `t_role_menu` values (100059, 100002, 100058);
insert into `t_role_menu` values (100060, 100002, 100069);
+insert into `t_role_menu` values (100061, 100002, 100071);
+insert into `t_role_menu` values (100062, 100002, 100072);
+insert into `t_role_menu` values (100063, 100002, 100073);
+insert into `t_role_menu` values (100064, 100002, 100074);
+
-- ----------------------------
-- Records of t_setting
diff --git a/streampark-console/streampark-console-service/src/main/assembly/script/data/pgsql-data.sql b/streampark-console/streampark-console-service/src/main/assembly/script/data/pgsql-data.sql
index d01733fbd..e1791aac2 100644
--- a/streampark-console/streampark-console-service/src/main/assembly/script/data/pgsql-data.sql
+++ b/streampark-console/streampark-console-service/src/main/assembly/script/data/pgsql-data.sql
@@ -114,6 +114,10 @@ insert into "public"."t_menu" values (100067, 100053, 'view', NULL, NULL, 'varia
insert into "public"."t_menu" values (100068, 100033, 'view', null, null, 'setting:view', null, '1', '1', null, now(), now());
insert into "public"."t_menu" values (100069, 100053, 'depend view', null, null, 'variable:depend_apps', null, '1', '1', NULL, now(), now());
insert into "public"."t_menu" values (100070, 100015, 'savepoint trigger', null, null, 'savepoint:trigger', null, '1', '1', null, now(), now());
+insert into "public"."t_menu" values (100071, 100033, 'link view', null, null, 'externalLink:view', null, '1', '1', NULL, now(), now());
+insert into "public"."t_menu" values (100072, 100033, 'link create', null, null, 'externalLink:create', null, '1', '1', NULL, now(), now());
+insert into "public"."t_menu" values (100073, 100033, 'link update', null, null, 'externalLink:update', null, '1', '1', NULL, now(), now());
+insert into "public"."t_menu" values (100074, 100033, 'link delete', null, null, 'externalLink:delete', null, '1', '1', NULL, now(), now());
-- ----------------------------
-- Records of t_role
@@ -184,6 +188,10 @@ insert into "public"."t_role_menu" values (100057, 100001, 100069);
insert into "public"."t_role_menu" values (100058, 100002, 100057);
insert into "public"."t_role_menu" values (100059, 100002, 100058);
insert into "public"."t_role_menu" values (100060, 100002, 100069);
+insert into "public"."t_role_menu" values (100061, 100002, 100071);
+insert into "public"."t_role_menu" values (100062, 100002, 100072);
+insert into "public"."t_role_menu" values (100063, 100002, 100073);
+insert into "public"."t_role_menu" values (100064, 100002, 100074);
-- ----------------------------
-- Records of t_setting
diff --git a/streampark-console/streampark-console-service/src/main/assembly/script/schema/mysql-schema.sql b/streampark-console/streampark-console-service/src/main/assembly/script/schema/mysql-schema.sql
index 680a44cb9..aa3d4a8e1 100644
--- a/streampark-console/streampark-console-service/src/main/assembly/script/schema/mysql-schema.sql
+++ b/streampark-console/streampark-console-service/src/main/assembly/script/schema/mysql-schema.sql
@@ -492,5 +492,18 @@ create table `t_alert_config` (
index `inx_alert_user` (`user_id`) using btree
) engine = innodb default charset = utf8mb4 collate = utf8mb4_general_ci;
+-- ----------------------------
+-- Table of t_external_link
+-- ----------------------------
+drop table if exists `t_external_link`;
+CREATE TABLE `t_external_link` (
+ `id` bigint not null auto_increment primary key,
+ `badge_label` varchar(100) collate utf8mb4_general_ci default null,
+ `badge_name` varchar(100) collate utf8mb4_general_ci default null,
+ `badge_color` varchar(100) collate utf8mb4_general_ci default null,
+ `link_url` varchar(1000) collate utf8mb4_general_ci default null,
+ `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'create time',
+ `modify_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'modify time'
+) engine = innodb default charset=utf8mb4 collate=utf8mb4_general_ci;
set foreign_key_checks = 1;
diff --git a/streampark-console/streampark-console-service/src/main/assembly/script/schema/pgsql-schema.sql b/streampark-console/streampark-console-service/src/main/assembly/script/schema/pgsql-schema.sql
index a41dec2b0..b6fd7e004 100644
--- a/streampark-console/streampark-console-service/src/main/assembly/script/schema/pgsql-schema.sql
+++ b/streampark-console/streampark-console-service/src/main/assembly/script/schema/pgsql-schema.sql
@@ -42,6 +42,7 @@ drop table if exists "public"."t_access_token";
drop table if exists "public"."t_flink_log";
drop table if exists "public"."t_team";
drop table if exists "public"."t_variable";
+drop table if exists "public"."t_external_link";
-- ----------------------------
-- drop sequence if exists
@@ -66,6 +67,7 @@ drop sequence if exists "public"."streampark_t_access_token_id_seq";
drop sequence if exists "public"."streampark_t_flink_log_id_seq";
drop sequence if exists "public"."streampark_t_team_id_seq";
drop sequence if exists "public"."streampark_t_variable_id_seq";
+drop sequence if exists "public"."streampark_t_external_link_id_seq";
-- ----------------------------
-- drop trigger if exists
@@ -741,6 +743,25 @@ create index "un_user_role_inx" on "public"."t_member" using btree (
"role_id" "pg_catalog"."int8_ops" asc nulls last
);
+
+-- ----------------------------
+-- table structure for t_external_link
+-- ----------------------------
+create sequence "public"."streampark_t_external_link_id_seq"
+ increment 1 start 10000 cache 1 minvalue 10000 maxvalue 9223372036854775807;
+
+create table "public"."t_external_link" (
+ "id" int8 not null default nextval('streampark_t_external_link_id_seq'::regclass),
+ "badge_label" varchar(100) collate "pg_catalog"."default",
+ "badge_name" varchar(100) collate "pg_catalog"."default",
+ "badge_color" varchar(100) collate "pg_catalog"."default",
+ "link_url" varchar(1000) collate "pg_catalog"."default",
+ "create_time" timestamp(6) not null default timezone('UTC-8'::text, (now())::timestamp(0) without time zone),
+ "modify_time" timestamp(6) not null default timezone('UTC-8'::text, (now())::timestamp(0) without time zone))
+;
+alter table "public"."t_external_link" add constraint "t_external_link_pkey" primary key ("id");
+
+
-- -----------------------------------------
-- trigger for table with modify_time field
-- -----------------------------------------
diff --git a/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.0.sql b/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.0.sql
index 6b818a927..5c2a9b5e9 100644
--- a/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.0.sql
+++ b/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.0.sql
@@ -15,6 +15,11 @@
* limitations under the License.
*/
+use streampark;
+
+set names utf8mb4;
+set foreign_key_checks = 0;
+
-- ISSUE-2192 DDL & DML Start
alter table `t_flink_savepoint` modify column `path` varchar(1024) collate utf8mb4_general_ci default null;
@@ -28,3 +33,29 @@ insert into `t_menu` values (100070, 100015, 'savepoint trigger', null, null, 's
alter table `t_flink_app` change column `launch` `release` tinyint default 1;
update `t_menu` set `menu_name`='release',`perms` = 'app:release' where `menu_id` = 100025;
-- ISSUE-2366 DDL & DML End
+
+-- Issue-2191/2215 Start
+drop table if exists `t_external_link`;
+CREATE TABLE `t_external_link` (
+ `id` bigint not null auto_increment primary key,
+ `badge_label` varchar(100) collate utf8mb4_general_ci default null,
+ `badge_name` varchar(100) collate utf8mb4_general_ci default null,
+ `badge_color` varchar(100) collate utf8mb4_general_ci default null,
+ `link_url` varchar(1000) collate utf8mb4_general_ci default null,
+ `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'create time',
+ `modify_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'modify time'
+) engine = innodb default charset=utf8mb4 collate=utf8mb4_general_ci;
+
+insert into `t_menu` values (100071, 100033, 'link view', null, null, 'externalLink:view', null, '1', 1, NULL, now(), now());
+insert into `t_menu` values (100072, 100033, 'link create', null, null, 'externalLink:create', null, '1', 1, NULL, now(), now());
+insert into `t_menu` values (100073, 100033, 'link update', null, null, 'externalLink:update', null, '1', 1, NULL, now(), now());
+insert into `t_menu` values (100074, 100033, 'link delete', null, null, 'externalLink:delete', null, '1', 1, NULL, now(), now());
+
+insert into `t_role_menu` values (100061, 100002, 100071);
+insert into `t_role_menu` values (100062, 100002, 100072);
+insert into `t_role_menu` values (100063, 100002, 100073);
+insert into `t_role_menu` values (100064, 100002, 100074);
+-- Issue-2191/2215 DDL & DML End
+
+set foreign_key_checks = 1;
+
diff --git a/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/pgsql/2.1.0.sql b/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/pgsql/2.1.0.sql
index 04220caac..0bf853197 100644
--- a/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/pgsql/2.1.0.sql
+++ b/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/pgsql/2.1.0.sql
@@ -29,3 +29,34 @@ alter table "public"."t_flink_app" rename "launch" to "release";
update "public"."t_menu" set "menu_name"='release',"perms" = 'app:release' where "menu_id" = 100025;
-- ISSUE-2366 DDL & DML End
+
+-- Issue-2191/2215 Start
+drop table if exists "public"."t_external_link";
+drop sequence if exists "public"."streampark_t_external_link_id_seq";
+-- ----------------------------
+-- table structure for t_external_link
+-- ----------------------------
+create sequence "public"."streampark_t_external_link_id_seq"
+ increment 1 start 10000 cache 1 minvalue 10000 maxvalue 9223372036854775807;
+
+create table "public"."t_external_link" (
+ "id" int8 not null default nextval('streampark_t_external_link_id_seq'::regclass),
+ "badge_label" varchar(100) collate "pg_catalog"."default",
+ "badge_name" varchar(100) collate "pg_catalog"."default",
+ "badge_color" varchar(100) collate "pg_catalog"."default",
+ "link_url" varchar(1000) collate "pg_catalog"."default",
+ "create_time" timestamp(6) not null default timezone('UTC-8'::text, (now())::timestamp(0) without time zone),
+ "modify_time" timestamp(6) not null default timezone('UTC-8'::text, (now())::timestamp(0) without time zone))
+;
+alter table "public"."t_external_link" add constraint "t_external_link_pkey" primary key ("id");
+
+insert into "public"."t_menu" values (100071, 100033, 'link view', null, null, 'externalLink:view', null, '1', '1', NULL, now(), now());
+insert into "public"."t_menu" values (100072, 100033, 'link create', null, null, 'externalLink:create', null, '1', '1', NULL, now(), now());
+insert into "public"."t_menu" values (100073, 100033, 'link update', null, null, 'externalLink:update', null, '1', '1', NULL, now(), now());
+insert into "public"."t_menu" values (100074, 100033, 'link delete', null, null, 'externalLink:delete', null, '1', '1', NULL, now(), now());
+
+insert into "public"."t_role_menu" values (100061, 100002, 100071);
+insert into "public"."t_role_menu" values (100062, 100002, 100072);
+insert into "public"."t_role_menu" values (100063, 100002, 100073);
+insert into "public"."t_role_menu" values (100064, 100002, 100074);
+-- Issue-2191/2215 End
diff --git a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/ExternalLinkController.java b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/ExternalLinkController.java
new file mode 100644
index 000000000..306ac31ed
--- /dev/null
+++ b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/ExternalLinkController.java
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+package org.apache.streampark.console.core.controller;
+
+import org.apache.streampark.common.util.Utils;
+import org.apache.streampark.console.base.domain.RestResponse;
+import org.apache.streampark.console.core.entity.ExternalLink;
+import org.apache.streampark.console.core.service.ExternalLinkService;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+
+import java.util.List;
+
+@Api(tags = {"FLINK_EXTERNAL_LINK_TAG"})
+@Slf4j
+@Validated
+@RestController
+@RequestMapping("/flink/externalLink")
+public class ExternalLinkController {
+
+ @Autowired private ExternalLinkService externalLinkService;
+
+ @ApiOperation(value = "List externalLink")
+ @PostMapping("/list")
+ @RequiresPermissions("externalLink:view")
+ public RestResponse list() {
+ List<ExternalLink> externalLink = externalLinkService.list();
+ return RestResponse.success(externalLink);
+ }
+
+ @ApiOperation(value = "Render externalLink by appId")
+ @ApiImplicitParams({
+ @ApiImplicitParam(
+ name = "appId",
+ value = "application id",
+ required = true,
+ paramType = "query",
+ dataTypeClass = Long.class)
+ })
+ @PostMapping("/render")
+ public RestResponse render(
+ @NotNull(message = "The flink app id cannot be null") @RequestParam("appId") Long appId) {
+ List<ExternalLink> renderedExternalLink = externalLinkService.render(appId);
+ return RestResponse.success(renderedExternalLink);
+ }
+
+ @ApiOperation(value = "Create externalLink")
+ @PostMapping("/create")
+ @RequiresPermissions("externalLink:create")
+ public RestResponse create(@Valid ExternalLink externalLink) {
+ externalLinkService.create(externalLink);
+ return RestResponse.success();
+ }
+
+ @ApiOperation(value = "Update externalLink")
+ @PostMapping("/update")
+ @RequiresPermissions("externalLink:update")
+ public RestResponse update(@Valid ExternalLink externalLink) {
+ Utils.notNull(externalLink.getId(), "The link id cannot be null");
+ externalLinkService.update(externalLink);
+ return RestResponse.success();
+ }
+
+ @ApiOperation(value = "Delete externalLink")
+ @ApiImplicitParams({
+ @ApiImplicitParam(
+ name = "id",
+ value = "external link id",
+ required = true,
+ paramType = "query",
+ dataTypeClass = Long.class)
+ })
+ @DeleteMapping("/delete")
+ @RequiresPermissions("externalLink:delete")
+ public RestResponse delete(
+ @NotNull(message = "The link id cannot be null") @RequestParam("id") Long id) {
+ externalLinkService.delete(id);
+ return RestResponse.success();
+ }
+}
diff --git a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/entity/ExternalLink.java b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/entity/ExternalLink.java
new file mode 100644
index 000000000..2584b4fd8
--- /dev/null
+++ b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/entity/ExternalLink.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package org.apache.streampark.console.core.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+@TableName("t_external_link")
+public class ExternalLink implements Serializable {
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ private String badgeLabel;
+
+ @NotBlank(message = "{required}")
+ private String badgeName;
+
+ @NotBlank(message = "{required}")
+ private String badgeColor;
+
+ @NotBlank(message = "{required}")
+ private String linkUrl;
+
+ @TableField(exist = false)
+ private String renderedLinkUrl;
+
+ private Date createTime;
+
+ private Date modifyTime;
+}
diff --git a/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.0.sql b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/enums/PlaceholderType.java
similarity index 60%
copy from streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.0.sql
copy to streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/enums/PlaceholderType.java
index 6b818a927..8b1ffb884 100644
--- a/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.0.sql
+++ b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/enums/PlaceholderType.java
@@ -15,16 +15,25 @@
* limitations under the License.
*/
--- ISSUE-2192 DDL & DML Start
+package org.apache.streampark.console.core.enums;
-alter table `t_flink_savepoint` modify column `path` varchar(1024) collate utf8mb4_general_ci default null;
+import java.io.Serializable;
-insert into `t_menu` values (100070, 100015, 'savepoint trigger', null, null, 'savepoint:trigger', null, '1', 1, null, now(), now());
+/** configFile Type enum */
+public enum PlaceholderType implements Serializable {
+ JOB_ID("job_id"),
--- ISSUE-2192 DDL & DML End
+ JOB_NAME("job_name"),
+ YARN_ID("yarn_id");
--- ISSUE-2366 DDL & DML Start
-alter table `t_flink_app` change column `launch` `release` tinyint default 1;
-update `t_menu` set `menu_name`='release',`perms` = 'app:release' where `menu_id` = 100025;
--- ISSUE-2366 DDL & DML End
+ private final String name;
+
+ PlaceholderType(String name) {
+ this.name = name;
+ }
+
+ public String get() {
+ return this.name;
+ }
+}
diff --git a/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.0.sql b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/mapper/ExternalLinkMapper.java
similarity index 60%
copy from streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.0.sql
copy to streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/mapper/ExternalLinkMapper.java
index 6b818a927..0c1995e07 100644
--- a/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.0.sql
+++ b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/mapper/ExternalLinkMapper.java
@@ -15,16 +15,10 @@
* limitations under the License.
*/
--- ISSUE-2192 DDL & DML Start
+package org.apache.streampark.console.core.mapper;
-alter table `t_flink_savepoint` modify column `path` varchar(1024) collate utf8mb4_general_ci default null;
+import org.apache.streampark.console.core.entity.ExternalLink;
-insert into `t_menu` values (100070, 100015, 'savepoint trigger', null, null, 'savepoint:trigger', null, '1', 1, null, now(), now());
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
--- ISSUE-2192 DDL & DML End
-
-
--- ISSUE-2366 DDL & DML Start
-alter table `t_flink_app` change column `launch` `release` tinyint default 1;
-update `t_menu` set `menu_name`='release',`perms` = 'app:release' where `menu_id` = 100025;
--- ISSUE-2366 DDL & DML End
+public interface ExternalLinkMapper extends BaseMapper<ExternalLink> {}
diff --git a/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.0.sql b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/ExternalLinkService.java
similarity index 60%
copy from streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.0.sql
copy to streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/ExternalLinkService.java
index 6b818a927..7605dc8fa 100644
--- a/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.0.sql
+++ b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/ExternalLinkService.java
@@ -15,16 +15,21 @@
* limitations under the License.
*/
--- ISSUE-2192 DDL & DML Start
+package org.apache.streampark.console.core.service;
-alter table `t_flink_savepoint` modify column `path` varchar(1024) collate utf8mb4_general_ci default null;
+import org.apache.streampark.console.core.entity.ExternalLink;
-insert into `t_menu` values (100070, 100015, 'savepoint trigger', null, null, 'savepoint:trigger', null, '1', 1, null, now(), now());
+import com.baomidou.mybatisplus.extension.service.IService;
--- ISSUE-2192 DDL & DML End
+import java.util.List;
+public interface ExternalLinkService extends IService<ExternalLink> {
--- ISSUE-2366 DDL & DML Start
-alter table `t_flink_app` change column `launch` `release` tinyint default 1;
-update `t_menu` set `menu_name`='release',`perms` = 'app:release' where `menu_id` = 100025;
--- ISSUE-2366 DDL & DML End
+ void create(ExternalLink externalLink);
+
+ void delete(Long linkId);
+
+ void update(ExternalLink externalLink);
+
+ List<ExternalLink> render(Long appId);
+}
diff --git a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/ExternalLinkServiceImpl.java b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/ExternalLinkServiceImpl.java
new file mode 100644
index 000000000..41015e544
--- /dev/null
+++ b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/ExternalLinkServiceImpl.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+package org.apache.streampark.console.core.service.impl;
+
+import org.apache.streampark.common.util.Utils;
+import org.apache.streampark.console.core.entity.Application;
+import org.apache.streampark.console.core.entity.ExternalLink;
+import org.apache.streampark.console.core.enums.PlaceholderType;
+import org.apache.streampark.console.core.mapper.ExternalLinkMapper;
+import org.apache.streampark.console.core.service.ApplicationService;
+import org.apache.streampark.console.core.service.ExternalLinkService;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.PropertyPlaceholderHelper;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+
+@Slf4j
+@Service
+@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
+public class ExternalLinkServiceImpl extends ServiceImpl<ExternalLinkMapper, ExternalLink>
+ implements ExternalLinkService {
+
+ @Autowired private ApplicationService applicationService;
+
+ @Override
+ public void create(ExternalLink externalLink) {
+ if (!this.check(externalLink)) {
+ return;
+ }
+ externalLink.setCreateTime(new Date());
+ externalLink.setModifyTime(new Date());
+ externalLink.setId(null);
+ this.save(externalLink);
+ }
+
+ @Override
+ public void update(ExternalLink externalLink) {
+ if (!this.check(externalLink)) {
+ return;
+ }
+ externalLink.setModifyTime(new Date());
+ baseMapper.updateById(externalLink);
+ }
+
+ @Override
+ public void delete(Long linkId) {
+ baseMapper.deleteById(linkId);
+ }
+
+ @Override
+ public List<ExternalLink> render(Long appId) {
+ Application app = applicationService.getById(appId);
+ Utils.notNull(app, "Application doesn't exist");
+ List<ExternalLink> externalLink = this.list();
+ if (externalLink != null && externalLink.size() > 0) {
+ // Render the placeholder
+ externalLink.forEach(link -> this.renderLinkUrl(link, app));
+ }
+ return externalLink;
+ }
+
+ private void renderLinkUrl(ExternalLink link, Application app) {
+ HashMap<String, String> map = new HashMap();
+ map.put(PlaceholderType.JOB_ID.get(), app.getJobId());
+ map.put(PlaceholderType.JOB_NAME.get(), app.getJobName());
+ map.put(PlaceholderType.YARN_ID.get(), app.getAppId());
+ PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper("{", "}");
+ link.setRenderedLinkUrl(
+ propertyPlaceholderHelper.replacePlaceholders(link.getLinkUrl().trim(), map::get));
+ }
+
+ private boolean check(ExternalLink params) {
+ LambdaQueryWrapper<ExternalLink> queryWrapper = new LambdaQueryWrapper<ExternalLink>();
+ // badgeName and LinkUrl cannot be duplicated
+ queryWrapper.nested(
+ qw ->
+ qw.eq(ExternalLink::getBadgeName, params.getBadgeName())
+ .or()
+ .eq(ExternalLink::getLinkUrl, params.getLinkUrl()));
+ if (params.getId() != null) {
+ queryWrapper.and(qw -> qw.ne(ExternalLink::getId, params.getId()));
+ }
+ ExternalLink result = this.getOne(queryWrapper);
+ if (result == null) {
+ return true;
+ }
+ Utils.required(
+ !result.getBadgeName().equals(params.getBadgeName()),
+ String.format("The name: %s is already existing.", result.getBadgeName()));
+ Utils.required(
+ !result.getLinkUrl().equals(params.getLinkUrl()),
+ String.format("The linkUrl: %s is already existing.", result.getLinkUrl()));
+ return false;
+ }
+}
diff --git a/streampark-console/streampark-console-service/src/main/resources/db/data-h2.sql b/streampark-console/streampark-console-service/src/main/resources/db/data-h2.sql
index 6fb2487fd..61525007b 100644
--- a/streampark-console/streampark-console-service/src/main/resources/db/data-h2.sql
+++ b/streampark-console/streampark-console-service/src/main/resources/db/data-h2.sql
@@ -113,6 +113,11 @@ insert into `t_menu` values (100068, 100054, 'view', NULL, NULL, 'variable:view'
insert into `t_menu` values (100069, 100034, 'view', null, null, 'setting:view', null, '1', 1, null, now(), now());
insert into `t_menu` values (100070, 100054, 'depend view', null, null, 'variable:depend_apps', null, '1', 1, NULL, now(), now());
insert into `t_menu` values (100071, 100015, 'savepoint trigger', null, null, 'savepoint:trigger', null, '1', 1, null, now(), now());
+insert into `t_menu` values (100072, 100033, 'link view', null, null, 'externalLink:view', null, '1', 1, null, now(), now());
+insert into `t_menu` values (100073, 100033, 'link create', null, null, 'externalLink:create', null, '1', 1, null, now(), now());
+insert into `t_menu` values (100074, 100033, 'link update', null, null, 'externalLink:update', null, '1', 1, null, now(), now());
+insert into `t_menu` values (100075, 100033, 'link delete', null, null, 'externalLink:delete', null, '1', 1, null, now(), now());
+
-- ----------------------------
-- Records of t_role
@@ -183,6 +188,10 @@ insert into `t_role_menu` values (100057, 100001, 100069);
insert into `t_role_menu` values (100058, 100002, 100057);
insert into `t_role_menu` values (100059, 100002, 100058);
insert into `t_role_menu` values (100060, 100002, 100069);
+insert into `t_role_menu` values (100061, 100002, 100072);
+insert into `t_role_menu` values (100062, 100002, 100073);
+insert into `t_role_menu` values (100063, 100002, 100074);
+insert into `t_role_menu` values (100064, 100002, 100075);
-- ----------------------------
-- Records of t_setting
diff --git a/streampark-console/streampark-console-service/src/main/resources/db/schema-h2.sql b/streampark-console/streampark-console-service/src/main/resources/db/schema-h2.sql
index 0ec3d38c7..d3300dd22 100644
--- a/streampark-console/streampark-console-service/src/main/resources/db/schema-h2.sql
+++ b/streampark-console/streampark-console-service/src/main/resources/db/schema-h2.sql
@@ -441,3 +441,18 @@ create table if not exists `t_access_token` (
`modify_time` datetime not null default current_timestamp on update current_timestamp comment 'modify time',
primary key(`id`)
);
+
+
+-- ----------------------------
+-- Table of t_external_link definition
+-- ----------------------------
+create table if not exists `t_external_link` (
+ `id` int generated by default as identity not null comment 'key',
+ `badge_label` varchar(100) default null,
+ `badge_name` varchar(100) default null,
+ `badge_color` varchar(100) default null,
+ `link_url` varchar(1000) default null,
+ `create_time` datetime not null default current_timestamp comment 'create time',
+ `modify_time` datetime not null default current_timestamp on update current_timestamp comment 'modify time',
+ primary key(`id`)
+);
diff --git a/streampark-console/streampark-console-service/src/main/resources/mapper/core/ExternalLinkMapper.xml b/streampark-console/streampark-console-service/src/main/resources/mapper/core/ExternalLinkMapper.xml
new file mode 100644
index 000000000..d77e08a13
--- /dev/null
+++ b/streampark-console/streampark-console-service/src/main/resources/mapper/core/ExternalLinkMapper.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="org.apache.streampark.console.core.mapper.ExternalLinkMapper">
+ <resultMap id="BaseResultMap" type="org.apache.streampark.console.core.entity.ExternalLink">
+ <id column="id" jdbcType="BIGINT" property="id"/>
+ <result column="badge_label" jdbcType="VARCHAR" property="badgeLabel"/>
+ <result column="badge_name" jdbcType="VARCHAR" property="badgeName"/>
+ <result column="badge_color" jdbcType="VARCHAR" property="badgeColor"/>
+ <result column="link_url" jdbcType="VARCHAR" property="linkUrl"/>
+ <result column="create_time" jdbcType="DATE" property="createTime"/>
+ <result column="modify_time" jdbcType="DATE" property="modifyTime"/>
+ </resultMap>
+</mapper>
diff --git a/streampark-console/streampark-console-webapp/src/api/flink/setting/externalLink.ts b/streampark-console/streampark-console-webapp/src/api/flink/setting/externalLink.ts
new file mode 100644
index 000000000..0dd3e0a1f
--- /dev/null
+++ b/streampark-console/streampark-console-webapp/src/api/flink/setting/externalLink.ts
@@ -0,0 +1,74 @@
+/*
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { AxiosResponse } from 'axios';
+import { ExternalLink } from './types/externalLink.type';
+import { Result } from '/#/axios';
+import { defHttp } from '/@/utils/http/axios';
+
+enum EXTERNAL_LINK_API {
+ LIST = '/flink/externalLink/list',
+ CREATE = '/flink/externalLink/create',
+ UPDATE = '/flink/externalLink/update',
+ DELETE = '/flink/externalLink/delete',
+ RENDER = '/flink/externalLink/render',
+}
+/**
+ * Get external link settings
+ * @returns {Promise<ExternalLink[]>}
+ */
+export function fetchExternalLink(): Promise<ExternalLink[]> {
+ return defHttp.post({ url: EXTERNAL_LINK_API.LIST });
+}
+
+/**
+ * Create external link
+ * @param {ExternalLink} data
+ * @returns {Promise<AxiosResponse<Result<ExternalLink>>>}
+ */
+export function fetchExternalLinkCreate(
+ data: ExternalLink,
+): Promise<AxiosResponse<Result<ExternalLink>>> {
+ return defHttp.post({ url: EXTERNAL_LINK_API.CREATE, data }, { isReturnNativeResponse: true });
+}
+
+/**
+ * Update external link
+ * @param {ExternalLink} data
+ * @returns {Promise<AxiosResponse<Result<ExternalLink>>>}
+ */
+export function fetchExternalLinkUpdate(
+ data: ExternalLink,
+): Promise<AxiosResponse<Result<ExternalLink>>> {
+ return defHttp.post({ url: EXTERNAL_LINK_API.UPDATE, data }, { isReturnNativeResponse: true });
+}
+
+/**
+ * Delete external link
+ * @returns {Promise<AxiosResponse<Result>>}
+ */
+export function fetchExternalLinkDelete(id: string): Promise<AxiosResponse<Result>> {
+ return defHttp.delete(
+ { url: EXTERNAL_LINK_API.DELETE, data: { id } },
+ { isReturnNativeResponse: true },
+ );
+}
+
+export function fetchAppExternalLink(data: {
+ appId: string;
+}): Promise<AxiosResponse<Result<ExternalLink[]>>> {
+ return defHttp.post({ url: EXTERNAL_LINK_API.RENDER, data }, { isReturnNativeResponse: true });
+}
diff --git a/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.0.sql b/streampark-console/streampark-console-webapp/src/api/flink/setting/types/externalLink.type.ts
similarity index 56%
copy from streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.0.sql
copy to streampark-console/streampark-console-webapp/src/api/flink/setting/types/externalLink.type.ts
index 6b818a927..56a64d528 100644
--- a/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.0.sql
+++ b/streampark-console/streampark-console-webapp/src/api/flink/setting/types/externalLink.type.ts
@@ -6,7 +6,7 @@
* (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
+ * https://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,
@@ -14,17 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
--- ISSUE-2192 DDL & DML Start
-
-alter table `t_flink_savepoint` modify column `path` varchar(1024) collate utf8mb4_general_ci default null;
-
-insert into `t_menu` values (100070, 100015, 'savepoint trigger', null, null, 'savepoint:trigger', null, '1', 1, null, now(), now());
-
--- ISSUE-2192 DDL & DML End
-
-
--- ISSUE-2366 DDL & DML Start
-alter table `t_flink_app` change column `launch` `release` tinyint default 1;
-update `t_menu` set `menu_name`='release',`perms` = 'app:release' where `menu_id` = 100025;
--- ISSUE-2366 DDL & DML End
+export interface ExternalLink {
+ badgeName: string;
+ badgeLabel: string;
+ badgeColor: string;
+ linkUrl: string;
+ id?: string;
+ modifyTime?: number;
+ renderedLinkUrl?: string;
+ createTime?: string;
+}
diff --git a/streampark-console/streampark-console-webapp/src/locales/lang/en/flink/setting.ts b/streampark-console/streampark-console-webapp/src/locales/lang/en/flink/setting.ts
index 3bc7ac3b1..8f9dddd22 100644
--- a/streampark-console/streampark-console-webapp/src/locales/lang/en/flink/setting.ts
+++ b/streampark-console/streampark-console-webapp/src/locales/lang/en/flink/setting.ts
@@ -20,6 +20,7 @@ export default {
alertSetting: 'Alert Setting',
flinkHome: 'Flink Home',
flinkCluster: 'Flink Cluster',
+ externalLink: 'External Link',
},
systemSettingItems: {
mavenSetting: {
@@ -182,6 +183,30 @@ export default {
clusterId: 'ClusterId',
},
},
+ externalLink: {
+ externalLinkSetting: 'External Link',
+ form: {
+ badgeLabel: 'Label',
+ badgeLabelPlaceholder: 'Please enter label',
+ badgeName: 'Name',
+ badgeNamePlaceholder: 'Please enter name',
+ badgeNameIsRequired: 'Name is required',
+ badgeColor: 'Color',
+ badgeColorIsRequired: 'Color is required',
+ badgePreview: 'Preview',
+ linkUrl: 'Link',
+ linkUrlPlaceholder: 'Please enter link url',
+ linkUrlIsRequired: 'Link is required',
+ },
+ operateMessage: {
+ createLinkSuccessful: 'Successfully create external link!',
+ createLinkFailed: 'Create external link failed!',
+ updateLinkSuccessful: 'Successfully update external link!',
+ updateLinkFailed: 'Update external link failed!',
+ operationFailed: 'Operation failed',
+ },
+ confDeleteTitle: 'Are you sure delete this link record',
+ },
env: {
conf: 'Flink Conf',
sync: 'Sync Conf',
diff --git a/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/flink/setting.ts b/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/flink/setting.ts
index 8edc086b9..f0a177723 100644
--- a/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/flink/setting.ts
+++ b/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/flink/setting.ts
@@ -20,6 +20,7 @@ export default {
alertSetting: '告警设置',
flinkHome: 'Flink版本',
flinkCluster: 'Flink集群',
+ externalLink: '扩展链接 ',
},
systemSettingItems: {
mavenSetting: {
@@ -179,6 +180,30 @@ export default {
clusterId: '集群ID',
},
},
+ externalLink: {
+ externalLinkSetting: '扩展链接',
+ form: {
+ badgeLabel: '徽章标签',
+ badgeLabelPlaceholder: '请输入标签的名称',
+ badgeName: '徽章名字',
+ badgeNamePlaceholder: '请输入徽章名字',
+ badgeNameIsRequired: '徽章名字是必填',
+ badgeColor: '徽章颜色',
+ badgeColorIsRequired: '徽章颜色是必选',
+ badgePreview: '徽章预览',
+ linkUrl: '链接',
+ linkUrlPlaceholder: '请输入链接',
+ linkUrlIsRequired: '链接是必填',
+ },
+ operateMessage: {
+ createLinkSuccessful: '成功新建扩展链接!',
+ createLinkFailed: '创建扩展链接失败!',
+ updateLinkSuccessful: '成功更新扩展链接!',
+ updateLinkFailed: '更新扩展链接失败!',
+ operationFailed: '操作失败',
+ },
+ confDeleteTitle: '请确认删除该条记录',
+ },
env: {
conf: 'Flink 配置',
sync: '配置同步',
diff --git a/streampark-console/streampark-console-webapp/src/views/flink/app/Detail.vue b/streampark-console/streampark-console-webapp/src/views/flink/app/Detail.vue
index cd5610a18..757dd6e88 100644
--- a/streampark-console/streampark-console-webapp/src/views/flink/app/Detail.vue
+++ b/streampark-console/streampark-console-webapp/src/views/flink/app/Detail.vue
@@ -18,6 +18,8 @@
import { defineComponent } from 'vue';
import { AppStateEnum, ExecModeEnum } from '/@/enums/flinkEnum';
import { useI18n } from '/@/hooks/web/useI18n';
+ import { fetchAppExternalLink } from '/@/api/flink/setting/externalLink';
+ import { ExternalLink } from '/@/api/flink/setting/types/externalLink.type';
export default defineComponent({
name: 'ApplicationDetail',
});
@@ -28,10 +30,10 @@
import { Icon } from '/@/components/Icon';
import { useRoute, useRouter } from 'vue-router';
import { fetchBackUps, fetchGet, fetchOptionLog, fetchYarn } from '/@/api/flink/app/app';
- import { onUnmounted, reactive, h, unref, ref } from 'vue';
+ import { onUnmounted, reactive, h, unref, ref, onMounted, computed } from 'vue';
import { useIntervalFn, useClipboard } from '@vueuse/core';
import { AppListRecord } from '/@/api/flink/app/app.type';
- import { Tooltip, Divider } from 'ant-design-vue';
+ import { Tooltip, Divider, Space } from 'ant-design-vue';
import { handleView } from './utils';
import { Button } from '/@/components/Button';
import { getDescSchema } from './data/detail.data';
@@ -44,6 +46,7 @@
import DetailTab from './components/AppDetail/DetailTab.vue';
import { createDetailProviderContext } from './hooks/useDetailContext';
import { useDrawer } from '/@/components/Drawer';
+ import ExternalLinkBadge from '../setting/components/ExternalLinkBadge.vue';
const route = useRoute();
const router = useRouter();
@@ -55,6 +58,7 @@
const { t } = useI18n();
const yarn = ref('');
+ const externalLinks = ref<ExternalLink[]>([]);
const app = reactive<Partial<AppListRecord>>({});
const detailTabs = reactive({
showConf: false,
@@ -218,23 +222,45 @@
window.open(res);
}
+ async function getExternalLinks() {
+ const { data: links } = await fetchAppExternalLink({ appId: route.query.appId as string });
+ externalLinks.value = links.data;
+ }
+
+ onMounted(() => {
+ getExternalLinks();
+ });
+
onUnmounted(() => {
pause();
});
+
+ const appNotRunning = computed(
+ () => app.state !== AppStateEnum.RUNNING || (yarn.value === null && app.flinkRestUrl === null),
+ );
</script>
<template>
<PageWrapper content-full-height content-background contentClass="p-24px">
<div class="mb-15px">
<span class="app-bar">{{ t('flink.app.detail.detailTitle') }}</span>
+ <Space class="-mt-8px">
+ <div v-for="link in externalLinks" :key="link.id">
+ <ExternalLinkBadge
+ :label="link.badgeLabel"
+ :redirect="link.renderedLinkUrl"
+ :color="link.badgeColor"
+ :message="link.badgeName"
+ :disabled="appNotRunning"
+ />
+ </div>
+ </Space>
<a-button type="primary" shape="circle" @click="router.back()" class="float-right -mt-8px">
<Icon icon="ant-design:arrow-left-outlined" />
</a-button>
<a-button
type="danger"
@click="handleFlinkView"
- :disabled="
- app.state !== AppStateEnum.RUNNING || (yarn === null && app.flinkRestUrl === null)
- "
+ :disabled="appNotRunning"
class="float-right -mt-8px mr-20px"
>
<Icon icon="ant-design:cloud-outlined" />
diff --git a/streampark-console/streampark-console-webapp/src/views/flink/setting/View.vue b/streampark-console/streampark-console-webapp/src/views/flink/setting/View.vue
index 061770201..5ff8212bf 100644
--- a/streampark-console/streampark-console-webapp/src/views/flink/setting/View.vue
+++ b/streampark-console/streampark-console-webapp/src/views/flink/setting/View.vue
@@ -42,6 +42,15 @@
<FlinkClusterSetting />
</Card>
</TabPane>
+ <TabPane
+ :tab="t('flink.setting.settingTab.externalLink')"
+ key="externalLink"
+ v-if="hasPermission('externalLink:view')"
+ >
+ <Card :bordered="false" class="system-setting">
+ <ExternalLinkSetting />
+ </Card>
+ </TabPane>
</Tabs>
</div>
</PageWrapper>
@@ -54,12 +63,15 @@
import AlertSetting from './components/AlertSetting.vue';
import FlinkEnvSetting from './components/FlinkEnvSetting.vue';
import FlinkClusterSetting from './components/FlinkClusterSetting.vue';
+ import ExternalLinkSetting from './components/ExternalLinkSetting.vue';
import { useRoute } from 'vue-router';
import { useI18n } from '/@/hooks/web/useI18n';
+ import { usePermission } from '/@/hooks/web/usePermission';
const { t } = useI18n();
const TabPane = Tabs.TabPane;
const route = useRoute();
+ const { hasPermission } = usePermission();
const activeKey = ref<string>((route?.query?.activeKey as string) || 'system');
</script>
diff --git a/streampark-console/streampark-console-webapp/src/views/flink/setting/components/ExternalLinkBadge.vue b/streampark-console/streampark-console-webapp/src/views/flink/setting/components/ExternalLinkBadge.vue
new file mode 100644
index 000000000..805be146b
--- /dev/null
+++ b/streampark-console/streampark-console-webapp/src/views/flink/setting/components/ExternalLinkBadge.vue
@@ -0,0 +1,61 @@
+<!--
+ 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
+
+ https://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.
+-->
+<template>
+ <div>
+ <a :href="redirect" @click.prevent="handleClick" :class="{ 'badge-disabled': disabled }">
+ <span v-if="hasLabel" class="badge-link">{{ label }}</span>
+ <span class="badge-link" :style="{ backgroundColor: color }">
+ {{ message }}
+ </span>
+ </a>
+ </div>
+</template>
+
+<script setup lang="ts">
+ import { computed } from 'vue';
+ interface ExternalLinkBadgeProps {
+ label: String;
+ message: String;
+ color?: string;
+ redirect?: string;
+ disabled?: boolean;
+ onBadgeClick?: () => void;
+ }
+ const props = defineProps<ExternalLinkBadgeProps>();
+ const hasLabel = computed(() => !!props.label);
+ const isClickable = computed(() => !!props.redirect && !props.disabled);
+ function handleClick() {
+ props.onBadgeClick?.() ?? (isClickable.value && window.open(props.redirect, '_blank'));
+ }
+</script>
+<style lang="less">
+ .badge-link {
+ display: inline-flexbox;
+ align-items: center;
+ justify-content: center;
+ color: #fff;
+ background-color: #484f58;
+ padding: 4px;
+ min-width: 40px;
+ }
+ .badge-disabled {
+ opacity: 0.5;
+ &:hover {
+ cursor: not-allowed;
+ }
+ }
+</style>
diff --git a/streampark-console/streampark-console-webapp/src/views/flink/setting/components/ExternalLinkModal.vue b/streampark-console/streampark-console-webapp/src/views/flink/setting/components/ExternalLinkModal.vue
new file mode 100644
index 000000000..69d6965bf
--- /dev/null
+++ b/streampark-console/streampark-console-webapp/src/views/flink/setting/components/ExternalLinkModal.vue
@@ -0,0 +1,196 @@
+<!--
+ 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
+
+ https://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.
+-->
+
+<script lang="ts">
+ import { defineComponent } from 'vue';
+ export default defineComponent({
+ name: 'ExternalLinkModal',
+ });
+</script>
+<script lang="ts" setup>
+ import { BasicForm, useForm } from '/@/components/Form';
+ import { BasicModal, useModalInner } from '/@/components/Modal';
+ import { useI18n } from '/@/hooks/web/useI18n';
+ import { Icon } from '/@/components/Icon';
+ import {
+ fetchExternalLinkCreate,
+ fetchExternalLinkUpdate,
+ } from '/@/api/flink/setting/externalLink';
+
+ import { useMessage } from '/@/hooks/web/useMessage';
+ import { ExternalLink } from '/@/api/flink/setting/types/externalLink.type';
+ import { ResultEnum } from '/@/enums/httpEnum';
+ import { renderColorField, renderPreview } from '../hooks/useExternalLink';
+
+ import { h, ref } from 'vue';
+
+ const DEFAULT_BADGE_COLOR = '#eb8c34';
+ const emit = defineEmits(['reload', 'register']);
+ const { t } = useI18n();
+ const externalLinkId = ref<string | null>(null);
+ const { Swal } = useMessage();
+ const [registerForm, { validate, resetFields, setFieldsValue }] = useForm({
+ labelWidth: 120,
+ schemas: [
+ {
+ field: 'badgeLabel',
+ label: t('flink.setting.externalLink.form.badgeLabel'),
+ component: 'Input',
+ componentProps: {
+ placeholder: t('flink.setting.externalLink.form.badgeLabelPlaceholder'),
+ allowClear: true,
+ },
+ },
+ {
+ field: 'badgeName',
+ label: t('flink.setting.externalLink.form.badgeName'),
+ component: 'Input',
+ componentProps: {
+ placeholder: t('flink.setting.externalLink.form.badgeNamePlaceholder'),
+ allowClear: true,
+ },
+ rules: [
+ {
+ required: true,
+ message: t('flink.setting.externalLink.form.badgeNameIsRequired'),
+ },
+ ],
+ },
+ {
+ field: 'badgeColor',
+ label: t('flink.setting.externalLink.form.badgeColor'),
+ component: 'Input',
+ defaultValue: DEFAULT_BADGE_COLOR,
+ render: ({ model, field }) => renderColorField(model, field),
+ rules: [
+ {
+ required: true,
+ message: t('flink.setting.externalLink.form.badgeColorIsRequired'),
+ },
+ ],
+ },
+ {
+ field: 'preview',
+ label: t('flink.setting.externalLink.form.badgePreview'),
+ component: 'Input',
+ render: ({ model }) => renderPreview(model),
+ },
+ {
+ field: 'linkUrl',
+ label: t('flink.setting.externalLink.form.linkUrl'),
+ component: 'Input',
+ componentProps: {
+ placeholder: t('flink.setting.externalLink.form.linkUrlPlaceholder'),
+ allowClear: true,
+ },
+ afterItem: () =>
+ h(
+ 'span',
+ { class: 'conf-switch' },
+ 'Supported variables: {job_id}, {yarn_id}, {job_name},Example: https://grafana/flink-monitoring?var-JobId=var-JobId={job_id}',
+ ),
+ rules: [
+ {
+ required: true,
+ message: t('flink.setting.externalLink.form.linkUrlIsRequired'),
+ },
+ ],
+ },
+ ],
+ colon: true,
+ showActionButtonGroup: false,
+ labelCol: { lg: { span: 6, offset: 0 }, sm: { span: 6, offset: 0 } },
+ wrapperCol: { lg: { span: 16, offset: 0 }, sm: { span: 4, offset: 0 } },
+ baseColProps: { span: 24 },
+ });
+
+ const [registerModal, { closeModal, changeOkLoading }] = useModalInner((data: ExternalLink) => {
+ resetFields();
+ externalLinkId.value = '';
+ if (data && Object.keys(data).length > 0) {
+ externalLinkId.value = data.id;
+ setFieldsValue(data);
+ }
+ });
+
+ // submit new external link
+ async function handleSubmit() {
+ try {
+ changeOkLoading(true);
+ const formValue = await validate();
+ if (externalLinkId.value) {
+ const { data } = await fetchExternalLinkUpdate({
+ id: externalLinkId.value,
+ badgeLabel: formValue.badgeLabel,
+ badgeColor: formValue.badgeColor,
+ badgeName: formValue.badgeName,
+ linkUrl: formValue.linkUrl,
+ });
+ if (data.code === ResultEnum.SUCCESS) {
+ Swal.fire({
+ icon: 'success',
+ title: t('flink.setting.externalLink.operateMessage.updateLinkSuccessful'),
+ showConfirmButton: false,
+ timer: 2000,
+ });
+ } else {
+ Swal.fire({
+ icon: 'error',
+ title: t('flink.setting.externalLink.operateMessage.updateLinkFailed'),
+ text: data['message'].replaceAll(/\[StreamPark]/g, ''),
+ });
+ }
+ } else {
+ const { data } = await fetchExternalLinkCreate(formValue);
+ if (data.code === ResultEnum.SUCCESS) {
+ Swal.fire({
+ icon: 'success',
+ title: t('flink.setting.externalLink.operateMessage.createLinkSuccessful'),
+ showConfirmButton: false,
+ timer: 2000,
+ });
+ } else {
+ Swal.fire({
+ title: t('flink.setting.externalLink.operateMessage.createLinkFailed'),
+ text: data['message'].replaceAll(/\[StreamPark]/g, ''),
+ icon: 'error',
+ });
+ }
+ }
+ closeModal();
+ emit('reload');
+ } catch (error: any) {
+ console.error(error);
+ } finally {
+ changeOkLoading(false);
+ }
+ }
+</script>
+<template>
+ <BasicModal
+ @register="registerModal"
+ :ok-text="t('common.submitText')"
+ v-bind="$attrs"
+ @ok="handleSubmit"
+ >
+ <template #title>
+ <Icon icon="ant-design:link-outlined" color="blue" />
+ {{ t('flink.setting.externalLink.externalLinkSetting') }}
+ </template>
+ <BasicForm @register="registerForm" />
+ </BasicModal>
+</template>
diff --git a/streampark-console/streampark-console-webapp/src/views/flink/setting/components/ExternalLinkSetting.vue b/streampark-console/streampark-console-webapp/src/views/flink/setting/components/ExternalLinkSetting.vue
new file mode 100644
index 000000000..cdeb9e854
--- /dev/null
+++ b/streampark-console/streampark-console-webapp/src/views/flink/setting/components/ExternalLinkSetting.vue
@@ -0,0 +1,128 @@
+<!--
+ 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
+
+ https://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.
+-->
+
+<script lang="ts">
+ import { defineComponent } from 'vue';
+
+ import { useI18n } from '/@/hooks/web/useI18n';
+ import { ExternalLink } from '/@/api/flink/setting/types/externalLink.type';
+ import { useMessage } from '/@/hooks/web/useMessage';
+
+ export default defineComponent({
+ name: 'ExternalLinkSetting',
+ });
+</script>
+<script lang="ts" setup name="ExternalLinkSetting">
+ import { onMounted, ref } from 'vue';
+ import { PlusOutlined } from '@ant-design/icons-vue';
+ import { ColumnsType } from 'ant-design-vue/lib/table';
+ import { useModal } from '/@/components/Modal';
+ import ExternalLinkModal from './ExternalLinkModal.vue';
+ import ExternalLinkBadge from './ExternalLinkBadge.vue';
+ import { Table, Popconfirm } from 'ant-design-vue';
+ import { fetchExternalLink, fetchExternalLinkDelete } from '/@/api/flink/setting/externalLink';
+
+ const [registerLinkModal, { openModal: openLinkModal }] = useModal();
+ const { Swal } = useMessage();
+ const { t } = useI18n();
+
+ const externalLinks = ref<ExternalLink[]>([]);
+
+ /* Get external link list */
+ async function getExternalLink() {
+ const externalLinkList = await fetchExternalLink();
+ externalLinks.value = externalLinkList;
+ }
+
+ onMounted(() => {
+ getExternalLink();
+ });
+
+ /* delete external link */
+ async function handleDeleteExternalLink(id: string) {
+ try {
+ await fetchExternalLinkDelete(id);
+ Swal.fire({
+ icon: 'success',
+ title: 'Delete Alert Config successful!',
+ showConfirmButton: false,
+ timer: 2000,
+ });
+ await getExternalLink();
+ } catch (error: any) {
+ console.log(error);
+ }
+ }
+
+ /* Edit button */
+ function handleEditExternalLink(item: ExternalLink) {
+ openLinkModal(true, item);
+ }
+ const columns: ColumnsType<ExternalLink> = [
+ { key: 'badgeColor', dataIndex: 'badgeColor' },
+ { key: 'linkUrl', dataIndex: 'linkUrl', ellipsis: true },
+ {
+ key: 'action',
+ dataIndex: 'action',
+ fixed: 'right',
+ align: 'right',
+ },
+ ];
+
+ const ATable = Table;
+ const APopconfirm = Popconfirm;
+</script>
+<template>
+ <div v-auth="'externalLink:create'" style="margin-bottom: 20px">
+ <a-button type="dashed" style="width: 100%; margin-top: 20px" @click="openLinkModal(true, {})">
+ <plus-outlined />
+ {{ t('common.add') }}
+ </a-button>
+ </div>
+ <a-table :showHeader="false" :data-source="externalLinks" :columns="columns" :pagination="false">
+ <template #bodyCell="{ column, record }">
+ <template v-if="column.key === 'badgeColor'">
+ <ExternalLinkBadge
+ :color="record.badgeColor"
+ :label="record.badgeLabel"
+ :redirect="record.linkUrl"
+ :message="record.badgeName"
+ />
+ </template>
+ <template v-else-if="column.key === 'action'">
+ <span>
+ <a-button
+ v-auth="'externalLink:update'"
+ type="link"
+ @click="handleEditExternalLink(record)"
+ >{{ t('common.edit') }}</a-button
+ >
+ <a-popconfirm
+ :title="t('flink.setting.externalLink.confDeleteTitle')"
+ @confirm="handleDeleteExternalLink(record.id)"
+ placement="topRight"
+ >
+ <a-button v-auth="'externalLink:delete'" danger type="text">{{
+ t('common.delText')
+ }}</a-button>
+ </a-popconfirm>
+ </span>
+ </template>
+ </template>
+ </a-table>
+ <ExternalLinkModal @register="registerLinkModal" width="850px" @reload="getExternalLink" />
+</template>
diff --git a/streampark-console/streampark-console-webapp/src/views/flink/setting/hooks/useExternalLink.tsx b/streampark-console/streampark-console-webapp/src/views/flink/setting/hooks/useExternalLink.tsx
new file mode 100644
index 000000000..74da072d8
--- /dev/null
+++ b/streampark-console/streampark-console-webapp/src/views/flink/setting/hooks/useExternalLink.tsx
@@ -0,0 +1,65 @@
+/*
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Input, Space } from 'ant-design-vue';
+
+import ExternalLinkBadge from '../components/ExternalLinkBadge.vue';
+import { useI18n } from '/@/hooks/web/useI18n';
+
+const { t } = useI18n();
+
+const DEFAULT_BLUE_HEX = '#2970e3';
+const DEFAULT_GREEN_HEX = '#10b53f';
+
+export const renderColorField = (formModel: Recordable, field: string) => {
+ const handlePromptColorUpdate = (color: string) => () => {
+ formModel[field] = color;
+ };
+ return (
+ <Space>
+ <Input
+ style="width:200px"
+ type="color"
+ allowClear
+ value={formModel[field]}
+ onInput={(e) => (formModel[field] = e.target.value)}
+ />
+ <ExternalLinkBadge
+ label={t('flink.setting.externalLink.form.badgeLabel')}
+ message={t('flink.setting.externalLink.form.badgeName')}
+ color={DEFAULT_GREEN_HEX}
+ onBadgeClick={handlePromptColorUpdate(DEFAULT_GREEN_HEX)}
+ />
+ <ExternalLinkBadge
+ label="Fink"
+ message="Metrics"
+ color={DEFAULT_BLUE_HEX}
+ onBadgeClick={handlePromptColorUpdate(DEFAULT_BLUE_HEX)}
+ />
+ </Space>
+ );
+};
+
+export const renderPreview = (formModel: Recordable) => {
+ return (
+ <ExternalLinkBadge
+ label={formModel['badgeLabel']}
+ message={formModel['badgeName'] || t('flink.setting.externalLink.form.badgeName')}
+ color={formModel['badgeColor']}
+ />
+ );
+};