This is an automated email from the ASF dual-hosted git repository.
zuston pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-uniffle.git
The following commit(s) were added to refs/heads/master by this push:
new 3cf820b5d [#2048] feat(coordinator): Introduce dashboard page in
dashboard web ui (#2049)
3cf820b5d is described below
commit 3cf820b5d5aaba22e1b75f37c9dc4ab8e3919b9d
Author: maobaolong <[email protected]>
AuthorDate: Thu Aug 22 19:58:56 2024 +0800
[#2048] feat(coordinator): Introduce dashboard page in dashboard web ui
(#2049)
### What changes were proposed in this pull request?
Introduce dashboard page in dashboard web ui to show the version, start
time and other information about dashboard.
### Why are the changes needed?
Fix: #2048
### Does this PR introduce _any_ user-facing change?
Dashboard UI
### How was this patch tested?
<img width="1435" alt="image"
src="https://github.com/user-attachments/assets/410cae37-ecf3-47ea-a769-a290514dec45">
---
dashboard/pom.xml | 4 +
.../apache/uniffle/dashboard/web/Dashboard.java | 11 ++
.../org/apache/uniffle/dashboard/web/lombok.config | 21 +++
.../dashboard/web/resource/DashboardResource.java | 78 +++++++++++
.../dashboard/web/resource/WebResource.java | 5 +
.../WebResource.java => vo/DashboardConfVO.java} | 19 ++-
dashboard/src/main/webapp/src/api/api.js | 10 ++
.../src/main/webapp/src/components/LayoutPage.vue | 4 +
.../src/main/webapp/src/pages/DashboardPage.vue | 154 +++++++++++++++++++++
dashboard/src/main/webapp/src/router/index.js | 6 +
10 files changed, 301 insertions(+), 11 deletions(-)
diff --git a/dashboard/pom.xml b/dashboard/pom.xml
index cdf40439b..ddeb1eaf0 100644
--- a/dashboard/pom.xml
+++ b/dashboard/pom.xml
@@ -37,6 +37,10 @@
<groupId>org.apache.uniffle</groupId>
<artifactId>rss-server-common</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.projectlombok</groupId>
+ <artifactId>lombok</artifactId>
+ </dependency>
</dependencies>
<build>
diff --git
a/dashboard/src/main/java/org/apache/uniffle/dashboard/web/Dashboard.java
b/dashboard/src/main/java/org/apache/uniffle/dashboard/web/Dashboard.java
index f3d75c524..bb8d1e25b 100644
--- a/dashboard/src/main/java/org/apache/uniffle/dashboard/web/Dashboard.java
+++ b/dashboard/src/main/java/org/apache/uniffle/dashboard/web/Dashboard.java
@@ -53,6 +53,7 @@ import org.apache.uniffle.dashboard.web.utils.DashboardUtils;
public class Dashboard {
private static final Logger LOG = LoggerFactory.getLogger(Dashboard.class);
+ private final long startTimeMs;
private DashboardConf conf;
// Jetty Server
@@ -62,6 +63,7 @@ public class Dashboard {
public Dashboard(DashboardConf coordinatorConf) {
this.conf = coordinatorConf;
+ this.startTimeMs = System.currentTimeMillis();
initialization();
}
@@ -120,6 +122,7 @@ public class Dashboard {
servletHolder.setInitParameter(
ServerProperties.PROVIDER_PACKAGES,
"org.apache.uniffle.dashboard.web.resource");
contextHandler.setAttribute("coordinatorServerAddresses",
coordinatorServerAddresses);
+ contextHandler.setAttribute(Dashboard.class.getCanonicalName(), this);
return contextHandler;
}
@@ -159,4 +162,12 @@ public class Dashboard {
}
LOG.info("Dashboard http server started, listening on port {}", httpPort);
}
+
+ public long getStartTimeMs() {
+ return startTimeMs;
+ }
+
+ public DashboardConf getConf() {
+ return conf;
+ }
}
diff --git
a/dashboard/src/main/java/org/apache/uniffle/dashboard/web/lombok.config
b/dashboard/src/main/java/org/apache/uniffle/dashboard/web/lombok.config
new file mode 100644
index 000000000..d5b979224
--- /dev/null
+++ b/dashboard/src/main/java/org/apache/uniffle/dashboard/web/lombok.config
@@ -0,0 +1,21 @@
+#
+# 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.
+#
+clear lombok.allArgsConstructor.flagUsage
+clear lombok.anyConstructor.flagUsage
+clear lombok.noArgsConstructor.flagUsage
+clear lombok.data.flagUsage
+clear lombok.builder.flagUsage
diff --git
a/dashboard/src/main/java/org/apache/uniffle/dashboard/web/resource/DashboardResource.java
b/dashboard/src/main/java/org/apache/uniffle/dashboard/web/resource/DashboardResource.java
new file mode 100644
index 000000000..ac3f518dc
--- /dev/null
+++
b/dashboard/src/main/java/org/apache/uniffle/dashboard/web/resource/DashboardResource.java
@@ -0,0 +1,78 @@
+/*
+ * 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.uniffle.dashboard.web.resource;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.servlet.ServletContext;
+
+import org.apache.hbase.thirdparty.javax.ws.rs.GET;
+import org.apache.hbase.thirdparty.javax.ws.rs.Path;
+import org.apache.hbase.thirdparty.javax.ws.rs.Produces;
+import org.apache.hbase.thirdparty.javax.ws.rs.core.Context;
+import org.apache.hbase.thirdparty.javax.ws.rs.core.MediaType;
+
+import org.apache.uniffle.common.util.Constants;
+import org.apache.uniffle.common.web.resource.BaseResource;
+import org.apache.uniffle.common.web.resource.Response;
+import org.apache.uniffle.dashboard.web.Dashboard;
+import org.apache.uniffle.dashboard.web.config.DashboardConf;
+import org.apache.uniffle.dashboard.web.vo.DashboardConfVO;
+
+@Produces({MediaType.APPLICATION_JSON})
+public class DashboardResource extends BaseResource {
+
+ @Context protected ServletContext servletContext;
+
+ @GET
+ @Path("/conf")
+ public Response<List<DashboardConfVO>> getDashboardConf() {
+ return execute(
+ () -> {
+ DashboardConf conf = getDashboard().getConf();
+ Set<Map.Entry<String, Object>> allEntry = conf.getAll();
+ List<DashboardConfVO> dashboardConfVOs = new ArrayList<>();
+ for (Map.Entry<String, Object> stringObjectEntry : allEntry) {
+ DashboardConfVO result =
+ new DashboardConfVO(
+ stringObjectEntry.getKey(),
String.valueOf(stringObjectEntry.getValue()));
+ dashboardConfVOs.add(result);
+ }
+ return dashboardConfVOs;
+ });
+ }
+
+ @GET
+ @Path("/info")
+ public Response<Map<String, Object>> getDashboardInfo() {
+ return execute(
+ () -> {
+ Map<String, Object> dashboardInfo = new HashMap<>();
+ dashboardInfo.put("version", Constants.VERSION_AND_REVISION_SHORT);
+ dashboardInfo.put("startTime", getDashboard().getStartTimeMs());
+ return dashboardInfo;
+ });
+ }
+
+ private Dashboard getDashboard() {
+ return (Dashboard)
servletContext.getAttribute(Dashboard.class.getCanonicalName());
+ }
+}
diff --git
a/dashboard/src/main/java/org/apache/uniffle/dashboard/web/resource/WebResource.java
b/dashboard/src/main/java/org/apache/uniffle/dashboard/web/resource/WebResource.java
index aaab37001..cc9093007 100644
---
a/dashboard/src/main/java/org/apache/uniffle/dashboard/web/resource/WebResource.java
+++
b/dashboard/src/main/java/org/apache/uniffle/dashboard/web/resource/WebResource.java
@@ -28,4 +28,9 @@ public class WebResource {
public Class<CoordinatorResource> getGainCoordinatorsResource() {
return CoordinatorResource.class;
}
+
+ @Path("dashboard")
+ public Class<DashboardResource> getDashboardResource() {
+ return DashboardResource.class;
+ }
}
diff --git
a/dashboard/src/main/java/org/apache/uniffle/dashboard/web/resource/WebResource.java
b/dashboard/src/main/java/org/apache/uniffle/dashboard/web/vo/DashboardConfVO.java
similarity index 64%
copy from
dashboard/src/main/java/org/apache/uniffle/dashboard/web/resource/WebResource.java
copy to
dashboard/src/main/java/org/apache/uniffle/dashboard/web/vo/DashboardConfVO.java
index aaab37001..ccbea86b1 100644
---
a/dashboard/src/main/java/org/apache/uniffle/dashboard/web/resource/WebResource.java
+++
b/dashboard/src/main/java/org/apache/uniffle/dashboard/web/vo/DashboardConfVO.java
@@ -15,17 +15,14 @@
* limitations under the License.
*/
-package org.apache.uniffle.dashboard.web.resource;
+package org.apache.uniffle.dashboard.web.vo;
-import org.apache.hbase.thirdparty.javax.ws.rs.Path;
-import org.apache.hbase.thirdparty.javax.ws.rs.Produces;
-import org.apache.hbase.thirdparty.javax.ws.rs.core.MediaType;
+import lombok.AllArgsConstructor;
+import lombok.Data;
-@Path("web")
-@Produces({MediaType.APPLICATION_JSON})
-public class WebResource {
- @Path("coordinator")
- public Class<CoordinatorResource> getGainCoordinatorsResource() {
- return CoordinatorResource.class;
- }
+@Data
+@AllArgsConstructor
+public class DashboardConfVO {
+ private String argumentKey;
+ private String argumentValue;
}
diff --git a/dashboard/src/main/webapp/src/api/api.js
b/dashboard/src/main/webapp/src/api/api.js
index c0a827f44..8ed6f44d8 100644
--- a/dashboard/src/main/webapp/src/api/api.js
+++ b/dashboard/src/main/webapp/src/api/api.js
@@ -16,6 +16,16 @@
*/
import http from '@/utils/http'
+// Create a Dashboard information interface
+export function getDashboardInfo(params, headers) {
+ return http.get('/dashboard/info', params, headers, 1)
+}
+
+// Create a dashboard configuration file interface
+export function getDashboardConf(params, headers) {
+ return http.get('/dashboard/conf', params, headers, 1)
+}
+
// Create a Coordinator information interface
export function getCoordinatorServerInfo(params, headers) {
return http.get('/coordinator/info', params, headers, 0)
diff --git a/dashboard/src/main/webapp/src/components/LayoutPage.vue
b/dashboard/src/main/webapp/src/components/LayoutPage.vue
index ead2e7469..b96913022 100644
--- a/dashboard/src/main/webapp/src/components/LayoutPage.vue
+++ b/dashboard/src/main/webapp/src/components/LayoutPage.vue
@@ -36,6 +36,10 @@
<img src="../assets/uniffle-logo.png" alt="unffile" />
</div>
</el-menu-item>
+ <el-menu-item index="/dashboardpage">
+ <el-icon><House /></el-icon>
+ <span>Dashboard</span>
+ </el-menu-item>
<el-menu-item index="/coordinatorserverpage">
<el-icon><House /></el-icon>
<span>Coordinator</span>
diff --git a/dashboard/src/main/webapp/src/pages/DashboardPage.vue
b/dashboard/src/main/webapp/src/pages/DashboardPage.vue
new file mode 100644
index 000000000..ef268901e
--- /dev/null
+++ b/dashboard/src/main/webapp/src/pages/DashboardPage.vue
@@ -0,0 +1,154 @@
+<!--
+ ~ 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.
+ -->
+
+<template>
+ <div class="demo-collapse">
+ <el-collapse v-model="pageData.activeNames" accordion:false>
+ <el-collapse-item title="Dashboard" name="1">
+ <div>
+ <el-descriptions class="margin-top" :column="3" :size="size" border>
+ <el-descriptions-item>
+ <template #label>
+ <div class="cell-item">
+ <el-icon :style="iconStyle">
+ <Wallet />
+ </el-icon>
+ Version
+ </div>
+ </template>
+ {{ pageData.dashboardInfo.version}}
+ </el-descriptions-item>
+ <el-descriptions-item>
+ <template #label>
+ <div class="cell-item">
+ <el-icon :style="iconStyle">
+ <Wallet />
+ </el-icon>
+ Start Time
+ </div>
+ </template>
+ <template #default>
+ {{ dateFormatter(null, null, pageData.dashboardInfo.startTime)
}}
+ </template>
+ </el-descriptions-item>
+ </el-descriptions>
+ </div>
+ </el-collapse-item>
+ <el-collapse-item title="Dashboard Properties" name="2">
+ <el-table :data="filteredTableData" stripe style="width: 100%">
+ <el-table-column prop="argumentKey" label="Name" min-width="380" />
+ <el-table-column prop="argumentValue" label="Value" min-width="380"
:show-overflow-tooltip="true" />
+ <el-table-column align="right">
+ <template #header>
+ <el-input v-model="searchKeyword" size="small" placeholder="Type
to search" />
+ </template>
+ </el-table-column>
+ </el-table>
+ </el-collapse-item>
+ </el-collapse>
+ </div>
+</template>
+
+<script>
+import { ref, reactive, computed, onMounted } from 'vue'
+import {
+ getDashboardConf,
+ getDashboardInfo
+} from '@/api/api'
+import { dateFormatter } from '@/utils/common'
+
+export default {
+ setup() {
+ const pageData = reactive({
+ activeNames: ['1', '2'],
+ tableData: [],
+ dashboardInfo: {}
+ })
+
+ async function getDbConfPage() {
+ const res = await getDashboardConf()
+ pageData.tableData = res.data.data
+ }
+ async function getDbInfo() {
+ const res = await getDashboardInfo()
+ pageData.dashboardInfo = res.data.data
+ }
+
+ onMounted(() => {
+ getDbConfPage()
+ getDbInfo()
+ })
+
+ const size = ref('')
+ const iconStyle = computed(() => {
+ const marginMap = {
+ large: '8px',
+ default: '6px',
+ small: '4px'
+ }
+ return {
+ marginRight: marginMap[size.value] || marginMap.default
+ }
+ })
+ const blockMargin = computed(() => {
+ const marginMap = {
+ large: '32px',
+ default: '28px',
+ small: '24px'
+ }
+ return {
+ marginTop: marginMap[size.value] || marginMap.default
+ }
+ })
+
+ /**
+ * The following describes how to handle blacklist select events.
+ */
+ const searchKeyword = ref('')
+ const filteredTableData = computed(() => {
+ const keyword = searchKeyword.value.trim()
+ if (!keyword) {
+ return pageData.tableData
+ } else {
+ return pageData.tableData.filter((row) => {
+ return row.argumentValue.includes(keyword) ||
row.argumentKey.includes(keyword)
+ })
+ }
+ })
+
+ return {
+ pageData,
+ iconStyle,
+ blockMargin,
+ size,
+ filteredTableData,
+ searchKeyword,
+ dateFormatter
+ }
+ }
+}
+</script>
+<style>
+.cell-item {
+ display: flex;
+ align-items: center;
+}
+
+.margin-top {
+ margin-top: 20px;
+}
+</style>
diff --git a/dashboard/src/main/webapp/src/router/index.js
b/dashboard/src/main/webapp/src/router/index.js
index 16ecaadc2..f0b6bb8e7 100644
--- a/dashboard/src/main/webapp/src/router/index.js
+++ b/dashboard/src/main/webapp/src/router/index.js
@@ -17,12 +17,18 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import ApplicationPage from '@/pages/ApplicationPage.vue'
+import DashboardPage from '@/pages/DashboardPage.vue'
import CoordinatorServerPage from '@/pages/CoordinatorServerPage.vue'
import ShuffleServerPage from '@/pages/ShuffleServerPage.vue'
import ExcludeNodeList from '@/pages/serverstatus/ExcludeNodeList'
import NodeListPage from '@/pages/serverstatus/NodeListPage.vue'
const routes = [
+ {
+ path: '/dashboardpage',
+ name: 'dashboardpage',
+ component: DashboardPage
+ },
{
path: '/coordinatorserverpage',
name: 'coordinatorserverpage',