This is an automated email from the ASF dual-hosted git repository.
lzljs3620320 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-paimon-webui.git
The following commit(s) were added to refs/heads/main by this push:
new 190b1ea [UI] Encapsulate http request (#29)
190b1ea is described below
commit 190b1ea93afb8f526b58d90bbc08c1e829a73098
Author: s7monk <[email protected]>
AuthorDate: Wed Aug 16 11:44:48 2023 +0800
[UI] Encapsulate http request (#29)
---
README.md | 2 +-
paimon-web-api/pom.xml | 1 +
paimon-web-server/pom.xml | 17 +++
.../paimon/web/server/configrue/CorsConfig.java | 45 +++++++
.../paimon/web/server/configrue/JacksonConfig.java | 55 ++++++++
.../web/server/configrue/SaTokenConfigure.java | 5 +-
.../web/server/controller/CatalogController.java | 100 +++++++++++++++
.../paimon/web/server/data/model/CatalogInfo.java | 49 ++++++++
.../web/server/data/result/enums/Status.java | 4 +
.../paimon/web/server/mapper/CatalogMapper.java | 28 +++++
.../paimon/web/server/service/CatalogService.java | 35 ++++++
.../server/service/impl/CatalogServiceImpl.java | 40 ++++++
.../src/main/resources/application-dev.yml | 3 +
.../src/main/resources/application.yml | 2 +-
paimon-web-server/src/main/resources/db/ddl-h2.sql | 13 ++
paimon-web-server/src/main/resources/db/dml-h2.sql | 7 +-
.../src/main/resources/i18n/messages.properties | 2 +
paimon-web-ui/index.html | 2 +-
paimon-web-ui/package.json | 6 +-
paimon-web-ui/src/api/api.ts | 54 ++++++++
.../LeftContent/index.tsx => api/data.d.ts} | 39 +++---
.../catalog-tree.module.less => api/endpoints.ts} | 11 +-
paimon-web-ui/src/api/http.ts | 89 +++++++++++++
paimon-web-ui/src/app/App.less | 7 +-
.../src/components/Dropdown/CatalogDropdown.tsx | 2 +-
.../CatalogModalForm/CatalogForm/index.tsx | 104 +++++++++++++++
.../components/CatalogModalForm/index.tsx | 58 +++++++++
.../CatalogTree/catalog-tree.module.less | 2 +-
.../LeftContent/components/CatalogTree/index.tsx | 139 +++++----------------
.../Metadata/components/LeftContent/index.tsx | 61 ++++++++-
.../LeftContent/left-content.module.less | 8 ++
.../RightContent/components/TopPanel/Tab/index.tsx | 2 +-
.../TreeComponent/tree-component.module.less | 4 +
paimon-web-ui/src/store/catalogStore.ts | 94 ++++++++++++++
.../Catalog/data.d.ts} | 23 ++--
paimon-web-ui/tsconfig.json | 3 +-
paimon-web-ui/vite.config.ts | 15 ++-
pom.xml | 3 +
38 files changed, 976 insertions(+), 158 deletions(-)
diff --git a/README.md b/README.md
index 9249cd8..3744ba3 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Apache Paimon (incubating) Manager
+# Apache Paimon (incubating) Web UI
This repository is web ui for the [Apache Paimon](https://paimon.apache.org/)
project.
diff --git a/paimon-web-api/pom.xml b/paimon-web-api/pom.xml
index 6d6886d..24f8c1c 100644
--- a/paimon-web-api/pom.xml
+++ b/paimon-web-api/pom.xml
@@ -49,6 +49,7 @@ under the License.
<artifactId>paimon-web-common</artifactId>
<version>${project.version}</version>
</dependency>
+
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
diff --git a/paimon-web-server/pom.xml b/paimon-web-server/pom.xml
index 78d43f4..993f48e 100644
--- a/paimon-web-server/pom.xml
+++ b/paimon-web-server/pom.xml
@@ -34,6 +34,10 @@ under the License.
Paimon Web Server is a server for the [Apache
Paimon](https://paimon.apache.org/) project.
</description>
+ <properties>
+ <hadoop.version>2.8.5</hadoop.version>
+ </properties>
+
<dependencies>
<dependency>
<groupId>org.apache.paimon</groupId>
@@ -41,6 +45,12 @@ under the License.
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.paimon</groupId>
+ <artifactId>paimon-web-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
@@ -136,6 +146,13 @@ under the License.
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
+
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-client</artifactId>
+ <version>${hadoop.version}</version>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
<build>
diff --git
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/configrue/CorsConfig.java
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/configrue/CorsConfig.java
new file mode 100644
index 0000000..7fff0b2
--- /dev/null
+++
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/configrue/CorsConfig.java
@@ -0,0 +1,45 @@
+/*
+ *
+ * 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.paimon.web.server.configrue;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+/** CorsConfig. */
+@Slf4j
+// @Configuration
+public class CorsConfig {
+
+ @Bean
+ public CorsFilter corsFilter() {
+ CorsConfiguration corsConfiguration = new CorsConfiguration();
+ corsConfiguration.setAllowCredentials(true);
+ corsConfiguration.addAllowedHeader("*");
+ corsConfiguration.addAllowedMethod("*");
+ corsConfiguration.addExposedHeader("*");
+ corsConfiguration.addAllowedOrigin("http://127.0.0.1:5173");
+ UrlBasedCorsConfigurationSource source = new
UrlBasedCorsConfigurationSource();
+ source.registerCorsConfiguration("/**", corsConfiguration);
+ return new CorsFilter(source);
+ }
+}
diff --git
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/configrue/JacksonConfig.java
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/configrue/JacksonConfig.java
new file mode 100644
index 0000000..2f7b2fb
--- /dev/null
+++
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/configrue/JacksonConfig.java
@@ -0,0 +1,55 @@
+/*
+ *
+ * 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.paimon.web.server.configrue;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+
+import java.text.SimpleDateFormat;
+
+/** JacksonConfig. */
+@Configuration
+public class JacksonConfig {
+ @Bean
+ public ObjectMapper getJacksonObjectMapper(Jackson2ObjectMapperBuilder
builder) {
+ ObjectMapper objectMapper = builder.createXmlMapper(false).build();
+ // When the sequence is changed to json, all longs will be changed to
strings.
+ // Because the numeric type in js cannot contain all java long values,
precision will be
+ // lost after more than 16 digits.
+ SimpleModule simpleModule = new SimpleModule();
+ simpleModule.addSerializer(
+ Long.class,
com.fasterxml.jackson.databind.ser.std.ToStringSerializer.instance);
+ simpleModule.addSerializer(
+ Long.TYPE,
com.fasterxml.jackson.databind.ser.std.ToStringSerializer.instance);
+ objectMapper.registerModule(simpleModule);
+ // If there are more attributes during deserialization, no exception
will be thrown.
+
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
+ // Date format handling.
+ objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd
HH:mm:ss"));
+ objectMapper.setVisibility(PropertyAccessor.FIELD,
JsonAutoDetect.Visibility.ANY);
+ return objectMapper;
+ }
+}
diff --git
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/configrue/SaTokenConfigure.java
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/configrue/SaTokenConfigure.java
index 11bf305..2497037 100644
---
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/configrue/SaTokenConfigure.java
+++
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/configrue/SaTokenConfigure.java
@@ -21,17 +21,16 @@ package org.apache.paimon.web.server.configrue;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.stp.StpUtil;
-import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/** Sa-Token path config. */
-@Configuration
+// @Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SaInterceptor(handle ->
StpUtil.checkLogin()))
.addPathPatterns("/**")
- .excludePathPatterns("/api/login");
+ .excludePathPatterns("/***");
}
}
diff --git
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/CatalogController.java
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/CatalogController.java
new file mode 100644
index 0000000..ef954b4
--- /dev/null
+++
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/CatalogController.java
@@ -0,0 +1,100 @@
+/*
+ * 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.paimon.web.server.controller;
+
+import org.apache.paimon.web.api.catalog.CatalogCreator;
+import org.apache.paimon.web.server.data.model.CatalogInfo;
+import org.apache.paimon.web.server.data.result.R;
+import org.apache.paimon.web.server.data.result.enums.Status;
+import org.apache.paimon.web.server.service.CatalogService;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/** Catalog api controller. */
+@Slf4j
+@RestController
+@RequestMapping("/api/catalog")
+public class CatalogController {
+
+ @Autowired private CatalogService catalogService;
+
+ /**
+ * Create a filesystem catalog.
+ *
+ * @param catalogInfo The catalogInfo for the filesystem catalog.
+ * @return The created catalog.
+ */
+ @PostMapping("/createFilesystemCatalog")
+ public R<Void> createFilesystemCatalog(@RequestBody CatalogInfo
catalogInfo) {
+ if (!catalogService.checkCatalogNameUnique(catalogInfo)) {
+ return R.failed(Status.CATALOG_NAME_IS_EXIST,
catalogInfo.getCatalogName());
+ }
+
+ try {
+ CatalogCreator.createFilesystemCatalog(catalogInfo.getWarehouse());
+ return catalogService.save(catalogInfo) ? R.succeed() : R.failed();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return R.failed(Status.CATALOG_CREATE_ERROR);
+ }
+ }
+
+ /**
+ * Create a hive catalog.
+ *
+ * @param catalogInfo The information for the hive catalog.
+ * @return The created catalog.
+ */
+ @PostMapping("/createHiveCatalog")
+ public R<Void> createHiveCatalog(@RequestBody CatalogInfo catalogInfo) {
+ if (!catalogService.checkCatalogNameUnique(catalogInfo)) {
+ return R.failed(Status.CATALOG_NAME_IS_EXIST,
catalogInfo.getCatalogName());
+ }
+
+ try {
+ CatalogCreator.createHiveCatalog(
+ catalogInfo.getWarehouse(),
+ catalogInfo.getHiveUri(),
+ catalogInfo.getHiveConfDir());
+ return catalogService.save(catalogInfo) ? R.succeed() : R.failed();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return R.failed(Status.CATALOG_CREATE_ERROR);
+ }
+ }
+
+ /**
+ * Get all catalog information.
+ *
+ * @return The list of all catalogs.
+ */
+ @GetMapping("/getAllCatalogs")
+ public R<List<CatalogInfo>> getCatalog() {
+ List<CatalogInfo> catalogs = catalogService.list();
+ return R.succeed(catalogs);
+ }
+}
diff --git
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/CatalogInfo.java
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/CatalogInfo.java
new file mode 100644
index 0000000..a90885a
--- /dev/null
+++
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/CatalogInfo.java
@@ -0,0 +1,49 @@
+/*
+ * 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.paimon.web.server.data.model;
+
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+/** Catalog table model. */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@TableName("catalog")
+public class CatalogInfo extends BaseModel {
+
+ private String catalogType;
+
+ private String catalogName;
+
+ private String warehouse;
+
+ private String hiveUri;
+
+ private String hiveConfDir;
+
+ @TableLogic private boolean isDelete;
+}
diff --git
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
index e20edca..c8dc0d3 100644
---
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
+++
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
@@ -49,6 +49,10 @@ public enum Status {
MENU_IN_USED(10201, "menu.in.used"),
MENU_NAME_IS_EXIST(10202, "menu.name.exist"),
MENU_PATH_INVALID(10203, "menu.path.invalid"),
+
+ /** ------------catalog-----------------. */
+ CATALOG_NAME_IS_EXIST(10301, "catalog.name.exist"),
+ CATALOG_CREATE_ERROR(10302, "catalog.create.error"),
;
private final int code;
diff --git
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/mapper/CatalogMapper.java
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/mapper/CatalogMapper.java
new file mode 100644
index 0000000..6d4c922
--- /dev/null
+++
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/mapper/CatalogMapper.java
@@ -0,0 +1,28 @@
+/*
+ * 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.paimon.web.server.mapper;
+
+import org.apache.paimon.web.server.data.model.CatalogInfo;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/** Catalog table mapper. */
+@Mapper
+public interface CatalogMapper extends BaseMapper<CatalogInfo> {}
diff --git
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/CatalogService.java
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/CatalogService.java
new file mode 100644
index 0000000..ecba5a1
--- /dev/null
+++
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/CatalogService.java
@@ -0,0 +1,35 @@
+/*
+ * 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.paimon.web.server.service;
+
+import org.apache.paimon.web.server.data.model.CatalogInfo;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/** Catalog Service. */
+public interface CatalogService extends IService<CatalogInfo> {
+
+ /**
+ * Verify if the catalog name is unique.
+ *
+ * @param catalog catalog info
+ * @return result
+ */
+ boolean checkCatalogNameUnique(CatalogInfo catalog);
+}
diff --git
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/impl/CatalogServiceImpl.java
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/impl/CatalogServiceImpl.java
new file mode 100644
index 0000000..316459a
--- /dev/null
+++
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/impl/CatalogServiceImpl.java
@@ -0,0 +1,40 @@
+/*
+ * 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.paimon.web.server.service.impl;
+
+import org.apache.paimon.web.server.data.model.CatalogInfo;
+import org.apache.paimon.web.server.mapper.CatalogMapper;
+import org.apache.paimon.web.server.service.CatalogService;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/** CatalogServiceImpl. */
+@Service
+public class CatalogServiceImpl extends ServiceImpl<CatalogMapper, CatalogInfo>
+ implements CatalogService {
+
+ @Override
+ public boolean checkCatalogNameUnique(CatalogInfo catalog) {
+ int catalogId = catalog.getId() == null ? -1 : catalog.getId();
+ CatalogInfo info =
+ this.lambdaQuery().eq(CatalogInfo::getCatalogName,
catalog.getCatalogName()).one();
+ return info == null || info.getId() == catalogId;
+ }
+}
diff --git a/paimon-web-server/src/main/resources/application-dev.yml
b/paimon-web-server/src/main/resources/application-dev.yml
index 252158b..70756dd 100644
--- a/paimon-web-server/src/main/resources/application-dev.yml
+++ b/paimon-web-server/src/main/resources/application-dev.yml
@@ -14,6 +14,9 @@
# limitations under the License.
spring:
+ h2: #http://localhost:10088/h2-console
+ console:
+ enabled: true
datasource:
driver-class-name: org.h2.Driver
url:
jdbc:h2:mem:paimon;MODE=MySQL;DB_CLOSE_DELAY=-1;DATABASE_TO_LOWER=true;;NON_KEYWORDS=USER;INIT=runscript
from 'classpath:db/ddl-h2.sql'
diff --git a/paimon-web-server/src/main/resources/application.yml
b/paimon-web-server/src/main/resources/application.yml
index 4e702ad..8bd64dc 100644
--- a/paimon-web-server/src/main/resources/application.yml
+++ b/paimon-web-server/src/main/resources/application.yml
@@ -20,7 +20,7 @@ server:
spring:
# Paimon Web Service Name
application:
- name: PaimonManager
+ name: Paimon Web UI
profiles:
active: dev
messages:
diff --git a/paimon-web-server/src/main/resources/db/ddl-h2.sql
b/paimon-web-server/src/main/resources/db/ddl-h2.sql
index 4f406e1..fc6936f 100644
--- a/paimon-web-server/src/main/resources/db/ddl-h2.sql
+++ b/paimon-web-server/src/main/resources/db/ddl-h2.sql
@@ -101,4 +101,17 @@ CREATE TABLE if not exists `role_menu`
`create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'create
time',
`update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'update
time',
unique key `idx_role_menu` (`role_id`, `menu_id`)
+) engine = innodb;
+
+CREATE TABLE if not exists `catalog`
+(
+ `id` int(11) not null auto_increment primary key comment 'id',
+ `catalog_type` varchar(50) not null comment 'catalog type',
+ `catalog_name` varchar(100) not null comment 'catalog name',
+ `warehouse` varchar(200) not null comment 'warehouse',
+ `hive_uri` varchar(200) comment 'hive uri',
+ `hive_conf_dir` varchar(100) comment 'catalog name',
+ `is_delete` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'is delete',
+ `create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'create
time',
+ `update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'update
time'
) engine = innodb;
\ No newline at end of file
diff --git a/paimon-web-server/src/main/resources/db/dml-h2.sql
b/paimon-web-server/src/main/resources/db/dml-h2.sql
index a8b0c54..6b0cfa6 100644
--- a/paimon-web-server/src/main/resources/db/dml-h2.sql
+++ b/paimon-web-server/src/main/resources/db/dml-h2.sql
@@ -59,4 +59,9 @@ values (1, 1),
(1, 1001),
(1, 1002),
(1, 1003),
- (1, 1004);
\ No newline at end of file
+ (1, 1004);
+
+insert into `catalog` (catalog_type, catalog_name, warehouse)
+values ('filesystem', 'paimon', 'file:///D:/path/'),
+ ('filesystem', 'fts', 'file:///D:/path/'),
+ ('filesystem', 'streaming_warehouse', 'file:///D:/path/')
diff --git a/paimon-web-server/src/main/resources/i18n/messages.properties
b/paimon-web-server/src/main/resources/i18n/messages.properties
index ed08361..a96af9f 100644
--- a/paimon-web-server/src/main/resources/i18n/messages.properties
+++ b/paimon-web-server/src/main/resources/i18n/messages.properties
@@ -30,3 +30,5 @@ role.key.exist=This role key {0} is exist
menu.in.used=This menu is in used
menu.name.exist=This menu name is exist:{0}
menu.path.invalid=This menu path is invalid:{0}
+catalog.name.exist=This catalog name {0} is exist
+catalog.create.error=An exception is returned when calling the Paimon API to
create a Catalog.
diff --git a/paimon-web-ui/index.html b/paimon-web-ui/index.html
index 85d1141..6d0b55b 100644
--- a/paimon-web-ui/index.html
+++ b/paimon-web-ui/index.html
@@ -23,7 +23,7 @@ under the License.
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon_blue.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>Paimon Manager</title>
+ <title>Apache Paimon</title>
</head>
<body>
<div id="root"></div>
diff --git a/paimon-web-ui/package.json b/paimon-web-ui/package.json
index e9ef617..7fe6537 100644
--- a/paimon-web-ui/package.json
+++ b/paimon-web-ui/package.json
@@ -13,14 +13,18 @@
"@douyinfe/semi-icons": "^2.38.1",
"@douyinfe/semi-ui": "^2.38.1",
"@monaco-editor/react": "^4.5.1",
+ "@reduxjs/toolkit": "^1.9.5",
"@semi-bot/semi-theme-doucreator": "^1.0.18",
"@semi-bot/semi-theme-figma": "^0.1.6",
+ "axios": "^1.4.0",
+ "http-proxy-middleware": "^2.0.6",
"less": "^4.1.3",
"monaco-editor": "^0.40.0",
"react": "^18.2.0",
"react-bootstrap": "^2.8.0",
"react-dom": "^18.2.0",
- "react-icons": "^4.10.1"
+ "react-icons": "^4.10.1",
+ "zustand": "^4.4.1"
},
"devDependencies": {
"@types/node": "^20.3.2",
diff --git a/paimon-web-ui/src/api/api.ts b/paimon-web-ui/src/api/api.ts
new file mode 100644
index 0000000..679728c
--- /dev/null
+++ b/paimon-web-ui/src/api/api.ts
@@ -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. */
+
+import http from '@api/http'
+import {API_ENDPOINTS} from '@api/endpoints';
+import Result = API.Result;
+import {CatalogItemList} from "@src/types/Catalog/data";
+
+export const createFileSystemCatalog = async (catalogProp: Prop.CatalogProp)
=> {
+ try {
+ return await http.httpPost<Result<any>,
Prop.CatalogProp>(API_ENDPOINTS.CREATE_FILE_SYSTEM_CATALOG, catalogProp);
+ } catch (error) {
+ console.error('Failed to create catalog:', error);
+ }
+};
+
+export const createHiveCatalog = async (catalogProp: Prop.CatalogProp) => {
+ try {
+ return await http.httpPost<Result<any>,
Prop.CatalogProp>(API_ENDPOINTS.CREATE_HIVE_CATALOG, catalogProp);
+ } catch (error) {
+ console.error('Failed to create catalog:', error);
+ }
+};
+
+export const getAllCatalogs = async () => {
+ try {
+ return await http.httpGet<Result<CatalogItemList>,
null>(API_ENDPOINTS.GET_ALL_CATALOGS)
+ } catch (error: any) {
+ console.error('Failed to get catalogs:', error);
+ }
+}
+
+const Api = {
+ createFileSystemCatalog,
+ createHiveCatalog,
+ getAllCatalogs
+}
+
+export default Api;
+
diff --git a/paimon-web-ui/src/pages/Metadata/components/LeftContent/index.tsx
b/paimon-web-ui/src/api/data.d.ts
similarity index 58%
copy from paimon-web-ui/src/pages/Metadata/components/LeftContent/index.tsx
copy to paimon-web-ui/src/api/data.d.ts
index b2a016c..2381a78 100644
--- a/paimon-web-ui/src/pages/Metadata/components/LeftContent/index.tsx
+++ b/paimon-web-ui/src/api/data.d.ts
@@ -15,20 +15,31 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-import { IconPlus } from "@douyinfe/semi-icons";
-import CatalogTree from
"@pages/Metadata/components/LeftContent/components/CatalogTree";
-import styles from "./left-content.module.less";
+declare namespace API {
+ export interface Result<T> {
+ code: number;
+ msg: string;
+ data: T;
+ }
-const MetadataSidebar = () => {
- return(
- <div className={styles.container}>
- <div className={styles['add-catalog-container']}>
- <span>Catalog</span>
- <IconPlus/>
- </div>
- <CatalogTree/>
- </div>
- )
+ export interface PageData<T> {
+ records: T[];
+ page: number;
+ size: number;
+ total: number;
+ }
+
+ export type PageResult<T> = Result<PageData<T>>
}
-export default MetadataSidebar;
+declare namespace Prop {
+ // Catalog
+ export interface CatalogProp {
+ catalogName: string,
+ catalogType: string,
+ warehouse: string,
+ hiveUri: string,
+ hiveConfDir: string,
+ isDelete: boolean
+ }
+}
\ No newline at end of file
diff --git
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/catalog-tree.module.less
b/paimon-web-ui/src/api/endpoints.ts
similarity index 77%
copy from
paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/catalog-tree.module.less
copy to paimon-web-ui/src/api/endpoints.ts
index 9f178b6..dc7a79f 100644
---
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/catalog-tree.module.less
+++ b/paimon-web-ui/src/api/endpoints.ts
@@ -15,8 +15,9 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-.catalog-tree-input {
- background-color: var(--semi-color-bg-0);
- width: 347px;
- margin-left: -12px;
-}
\ No newline at end of file
+export const API_ENDPOINTS = {
+ // catalog
+ CREATE_FILE_SYSTEM_CATALOG: '/catalog/createFilesystemCatalog',
+ CREATE_HIVE_CATALOG: '/catalog/createHiveCatalog',
+ GET_ALL_CATALOGS: '/catalog/getAllCatalogs',
+};
\ No newline at end of file
diff --git a/paimon-web-ui/src/api/http.ts b/paimon-web-ui/src/api/http.ts
new file mode 100644
index 0000000..e1364c6
--- /dev/null
+++ b/paimon-web-ui/src/api/http.ts
@@ -0,0 +1,89 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import axios, { AxiosResponse, AxiosError } from 'axios';
+
+const httpClient = axios.create({
+ baseURL: '/api',
+ timeout: 30 * 1000,
+ withCredentials: false,
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+});
+
+// request interceptor.
+httpClient.interceptors.request.use(
+ (config: any) => {
+ // Here you can set request headers likeļ¼config.headers['Token'] =
localStorage.getItem('Token').
+ /* config.headers = {
+ "Authorization": store.getState().user.token
+ }*/
+ return config;
+ },
+ (error: AxiosError) => {
+ // Handle request errors
+ console.log('request:', error);
+ return Promise.reject(error);
+ }
+);
+
+// response interceptor.
+httpClient.interceptors.response.use(
+ (response: AxiosResponse) => {
+ // Here you can process the response data.
+ return response;
+ },
+ (error: AxiosError) => {
+ // Handle response errors.
+ console.log('response:', error);
+ return Promise.reject(error);
+ }
+);
+
+const httpGet = async <T, E>(url: string, param?: E): Promise<T> => {
+ const {data} = await httpClient.get<T>(url, {params: param})
+ return data
+}
+
+const httpPost = async <T, E>(url: string, param: E): Promise<T> => {
+ const {data} = await httpClient.post<T>(url, param)
+ return data
+}
+
+const httpDelete = async <T, E>(url: string, param: E): Promise<T> => {
+ const {data} = await httpClient.delete(url, {params: param})
+ return data
+}
+
+const httpFormPost = async <T, E>(url: string, params?: E): Promise<T> => {
+ const {data} = await httpClient.post(url, params, {
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ }
+ })
+ return data
+}
+
+const http = {
+ httpGet,
+ httpPost,
+ httpDelete,
+ httpFormPost
+}
+
+export default http;
\ No newline at end of file
diff --git a/paimon-web-ui/src/app/App.less b/paimon-web-ui/src/app/App.less
index aa5443c..e14bbbd 100644
--- a/paimon-web-ui/src/app/App.less
+++ b/paimon-web-ui/src/app/App.less
@@ -24,7 +24,10 @@ body,
padding: 0;
color: rgba(0, 0, 0, 0.65) !important;
//--semi-color-primary: rgba(var(--semi-violet-6), 1);
- --semi-color-primary: rgba(var(--semi-grey-6), 1);
- --semi-color-primary-hover: rgba(var(--semi-violet-3), 1);
+ //--semi-color-primary: rgba(var(--semi-grey-6), 1);
+ //--semi-color-primary: rgba(var(--semi-blue-5), 1);
+ //--semi-color-primary-hover: rgba(var(--semi-violet-3), 1);
+ //--semi-color-primary-hover: rgba(var(--semi-blue-6), 1);
+ //--semi-color-primary-active: rgba(var(--semi-blue-7), 1);
--semi-color-primary-light-default: rgba(var(--semi-color-bg-0), 1);
}
diff --git a/paimon-web-ui/src/components/Dropdown/CatalogDropdown.tsx
b/paimon-web-ui/src/components/Dropdown/CatalogDropdown.tsx
index 722c206..f533505 100644
--- a/paimon-web-ui/src/components/Dropdown/CatalogDropdown.tsx
+++ b/paimon-web-ui/src/components/Dropdown/CatalogDropdown.tsx
@@ -65,7 +65,7 @@ const CatalogDropdown: React.FC<CatalogDropdownProps> =
(props) => {
placeholder="Select Catalog"
optionList={renderCatalogList()}
showClear
- style={{display: "flex", flexGrow: 1, backgroundColor:
"var(--semi-color-bg-0)"}}
+ style={{display: "flex", flexGrow: 1}}
/>
</>
}
diff --git
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/CatalogForm/index.tsx
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/CatalogForm/index.tsx
new file mode 100644
index 0000000..55cd745
--- /dev/null
+++
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/CatalogForm/index.tsx
@@ -0,0 +1,104 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import { Form } from '@douyinfe/semi-ui';
+
+// @ts-ignore
+const CatalogForm = ({ getFormApi }) => {
+ let message = 'This item is required.';
+ return(
+ <>
+ <Form
+ wrapperCol={{ span: 20 }}
+ labelCol={{ span: 4 }}
+ labelPosition='left'
+ labelAlign='left'
+ getFormApi={getFormApi}
+ >
+ {
+ ({ formState }) => (
+ <>
+ <Form.Input
+ field="catalogName"
+ label='Catalog Name'
+ trigger='blur'
+ rules={[
+ { required: true, message },
+ ]}
+ style={{ width: "100%" }}/>
+
+ <Form.Select
+ field="catalogType"
+ label='Catalog Type'
+ placeholder={"please select catalog"}
+ rules={[
+ { required: true, message },
+ ]}
+ style={{ width: "100%" }}>
+ <Form.Select.Option
value="filesystem">FileSystem</Form.Select.Option>
+ <Form.Select.Option
value="hive">Hive</Form.Select.Option>
+ </Form.Select>
+
+ {formState.values.catalogType === 'filesystem' ? (
+ <Form.Input
+ field="warehouse"
+ label='Warehouse'
+ trigger='blur'
+ rules={[
+ { required: true, message },
+ ]}
+ style={{ width: "100%" }}/>
+ )
+ :
+ <>
+ <Form.Input
+ field="warehouse"
+ label='Warehouse'
+ trigger='blur'
+ rules={[
+ { required: true, message },
+ ]}
+ style={{ width: "100%" }}/>
+
+ <Form.Input
+ field="uri"
+ label='Hive Uri'
+ trigger='blur'
+ rules={[
+ { required: true, message },
+ ]}
+ style={{ width: "100%" }}/>
+
+ <Form.Input
+ field="hiveConfDir"
+ label='Hive Conf Dir'
+ trigger='blur'
+ rules={[
+ { required: true, message },
+ ]}
+ style={{ width: "100%" }}/>
+ </>
+ }
+ </>
+ )
+ }
+ </Form>
+ </>
+ );
+}
+
+export default CatalogForm;
\ No newline at end of file
diff --git
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/index.tsx
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/index.tsx
new file mode 100644
index 0000000..05780b1
--- /dev/null
+++
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogModalForm/index.tsx
@@ -0,0 +1,58 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import React, {useState} from "react";
+import { Modal } from '@douyinfe/semi-ui';
+import CatalogForm from
"@pages/Metadata/components/LeftContent/components/CatalogModalForm/CatalogForm";
+
+type CatalogModalFormProps = {
+ visible: boolean;
+ onClose: () => void;
+ onOk: (formApi: any) => void;
+};
+
+
+const CatalogModalForm: React.FC<CatalogModalFormProps> = ({ visible ,
onClose, onOk }) => {
+ const [formApi, setFormApi] = useState(null);
+
+ const getFormApi = (api: any) => {
+ setFormApi(api);
+ };
+
+ const handleOkClick = async () => {
+ await onOk(formApi);
+ onClose();
+ };
+
+ return(
+ <Modal
+ title="Create Catalog"
+ visible = {visible}
+ onCancel= {onClose}
+ maskClosable={false}
+ okText={'Submit'}
+ cancelText={'Cancel'}
+ width={'750px'}
+ height={'550px'}
+ onOk={() => handleOkClick()}
+ >
+ <CatalogForm getFormApi={getFormApi}/>
+ </Modal>
+ );
+}
+
+export default CatalogModalForm;
\ No newline at end of file
diff --git
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/catalog-tree.module.less
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/catalog-tree.module.less
index 9f178b6..4f3f847 100644
---
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/catalog-tree.module.less
+++
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/catalog-tree.module.less
@@ -16,7 +16,7 @@ specific language governing permissions and limitations
under the License. */
.catalog-tree-input {
- background-color: var(--semi-color-bg-0);
+ //background-color: var(--semi-color-bg-0);
width: 347px;
margin-left: -12px;
}
\ No newline at end of file
diff --git
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/index.tsx
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/index.tsx
index cc61ee8..e3fdd71 100644
---
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/index.tsx
+++
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/components/CatalogTree/index.tsx
@@ -16,116 +16,38 @@ specific language governing permissions and limitations
under the License. */
import {Input, Tree} from '@douyinfe/semi-ui';
-import { IconSearch } from "@douyinfe/semi-icons";
+import { IconSearch, IconFile } from "@douyinfe/semi-icons";
+import {useEffect, useState} from "react";
import styles from "./catalog-tree.module.less"
+import { useCatalogStore } from "@src/store/catalogStore.ts";
const CatalogTree = () => {
- const treeData = [
- {
- label: 'paimon_catalog_01',
- value: 'catalog01',
- key: '0',
- children: [
- {
- label: 'paimon_db_01',
- value: 'db01',
- key: '0-0',
- children: [
- {
- label: 'paimon_table_01',
- value: 'paimon_table_01',
- key: '0-0-0',
- },
- {
- label: 'paimon_table_02',
- value: 'paimon_table_02',
- key: '0-1-0',
- },
- {
- label: 'paimon_table_03',
- value: 'paimon_table_03',
- key: '0-2-0',
- },
- {
- label: 'paimon_table_04',
- value: 'paimon_table_04',
- key: '0-3-0',
- },
- {
- label: 'paimon_table_05',
- value: 'paimon_table_05',
- key: '0-4-0',
- },
- ],
- },
- {
- label: 'paimon_db_02',
- value: 'paimon_db_02',
- key: '0-1',
- children: [
- {
- label: 'Osaka',
- value: 'Osaka',
- key: '0-1-0'
- }
- ]
- },
- {
- label: 'paimon_db_03',
- value: 'paimon_db_03',
- key: '0-2',
- children: [
- {
- label: 'Beijing',
- value: 'Beijing',
- key: '0-2-0',
- },
- ],
- },
- {
- label: 'paimon_db_04',
- value: 'paimon_db_04',
- key: '0-3',
- children: [
- {
- label: 'Beijing',
- value: 'Beijing',
- key: '0-3-0',
- },
- ],
- },
- {
- label: 'paimon_db_05',
- value: 'paimon_db_05',
- key: '0-4',
- children: [
- {
- label: 'Beijing',
- value: 'Beijing',
- key: '0-4-0',
- },
- ],
- },
- ],
- },
- {
- label: 'paimon_catalog_02',
- value: 'paimon_catalog_02',
- key: '1',
- children: [
- {
- label: 'United States',
- value: 'United States',
- key: '1-0'
- },
- {
- label: 'Canada',
- value: 'Canada',
- key: '1-1'
- }
- ]
- }
- ];
+
+ type TreeDataItem = {
+ label: string;
+ value: string;
+ key: string;
+ children?: TreeDataItem[];
+ };
+
+ const [treeData, setTreeData] = useState<TreeDataItem[]>([]);
+ const fetchCatalogData = useCatalogStore(state => state.fetchCatalogData);
+ const catalogItemList = useCatalogStore(state => state.catalogItemList);
+
+ useEffect(() => {
+ // Fetch the catalog data when the component mounts
+ fetchCatalogData();
+ }, [fetchCatalogData]);
+
+ useEffect(() => {
+ // Update treeData when catalogItemList changes
+ const transformedData = catalogItemList.map(item => ({
+ label: item.catalogName,
+ value: item.catalogName,
+ key: item.id.toString(),
+ }));
+ setTreeData(transformedData);
+ }, [catalogItemList]);
const renderLabel = (x: any) => {
const className = x.className;
@@ -141,13 +63,12 @@ const CatalogTree = () => {
role="treeitem"
onClick={isLeaf ? onClick : onExpand}
>
- {isLeaf ? null : expandIcon}
+ {isLeaf ? <IconFile style={{marginRight: "8px", color:
"var(--semi-color-text-2)"}}/> : expandIcon}
<span>{label}</span>
</li>
);
};
-
return(
<Tree
filterTreeNode
diff --git a/paimon-web-ui/src/pages/Metadata/components/LeftContent/index.tsx
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/index.tsx
index b2a016c..b03a999 100644
--- a/paimon-web-ui/src/pages/Metadata/components/LeftContent/index.tsx
+++ b/paimon-web-ui/src/pages/Metadata/components/LeftContent/index.tsx
@@ -17,16 +17,75 @@ under the License. */
import { IconPlus } from "@douyinfe/semi-icons";
import CatalogTree from
"@pages/Metadata/components/LeftContent/components/CatalogTree";
+import { useState } from "react";
+import CatalogModalForm from
"@pages/Metadata/components/LeftContent/components/CatalogModalForm";
+import {useCatalogStore} from "@src/store/catalogStore.ts";
import styles from "./left-content.module.less";
const MetadataSidebar = () => {
+
+ const [showModal, setShowModal] = useState(false);
+ const createFilesystemCatalog = useCatalogStore(state =>
state.createFileSystemCatalog);
+ const createHiveCatalog = useCatalogStore(state =>
state.createHiveCatalog);
+ const fetchCatalogData = useCatalogStore(state => state.fetchCatalogData);
+
+ const handleOpenModal = () => {
+ setShowModal(true);
+ };
+
+ const handleCloseModal = () => {
+ setShowModal(false);
+ };
+
+ const handleOk = (formApi: any) => {
+ return new Promise<void>((resolve, reject) => {
+ formApi
+ .validate()
+ .then((values: any) => {
+ // Assuming values contains the form data
+ const formData = values;
+
+ const catalogProp: Prop.CatalogProp = {
+ catalogName: formData.catalogName,
+ catalogType: formData.catalogType,
+ warehouse: formData.warehouse,
+ hiveUri: formData.hiveUri,
+ hiveConfDir: formData.hiveConfDir,
+ isDelete: false
+ };
+
+ if (formData.catalogType === 'filesystem') {
+ createFilesystemCatalog(catalogProp)
+ .then(() => {
+ fetchCatalogData();
+ resolve();
+ })
+ } else {
+ createHiveCatalog(catalogProp)
+ .then(() => {
+ fetchCatalogData();
+ resolve();
+ })
+ }
+ resolve();
+ })
+ .catch((errors: any) => {
+ console.log(errors);
+ reject(errors);
+ });
+ });
+ };
+
return(
<div className={styles.container}>
<div className={styles['add-catalog-container']}>
<span>Catalog</span>
- <IconPlus/>
+ <IconPlus className={styles.iconPlus}
onClick={handleOpenModal}/>
</div>
<CatalogTree/>
+ {showModal && (
+ <CatalogModalForm visible={showModal}
onClose={handleCloseModal} onOk={handleOk}/>
+ )}
</div>
)
}
diff --git
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/left-content.module.less
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/left-content.module.less
index bcee825..862b5c5 100644
---
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/left-content.module.less
+++
b/paimon-web-ui/src/pages/Metadata/components/LeftContent/left-content.module.less
@@ -28,4 +28,12 @@ under the License. */
flex-direction: row;
justify-content: space-between;
align-items: center;
+}
+
+.iconPlus {
+ transition: box-shadow 0.1s ease-in-out;
+}
+
+.iconPlus:hover {
+ box-shadow: 0px 0px 30px rgba(var(--semi-light-blue-5), 1);
}
\ No newline at end of file
diff --git
a/paimon-web-ui/src/pages/Playground/components/RightContent/components/TopPanel/Tab/index.tsx
b/paimon-web-ui/src/pages/Playground/components/RightContent/components/TopPanel/Tab/index.tsx
index 0eb8710..3397ad3 100644
---
a/paimon-web-ui/src/pages/Playground/components/RightContent/components/TopPanel/Tab/index.tsx
+++
b/paimon-web-ui/src/pages/Playground/components/RightContent/components/TopPanel/Tab/index.tsx
@@ -40,7 +40,7 @@ const Index = () => {
return (
<Tabs type="card" defaultActiveKey="1" onTabClose={close}>
{tabList.map((t) => (
- <TabPane closable={t.closable} tab={<span><IconCode
style={{color: "rgba(var(--semi-violet-6), 1)"}}/>{t.tab}</span>}
itemKey={t.itemKey} key={t.itemKey}>
+ <TabPane closable={t.closable} tab={<span><IconCode
style={{color: "rgba(var(--semi-blue-5), 1)"}}/>{t.tab}</span>}
itemKey={t.itemKey} key={t.itemKey}>
{t.content}
</TabPane>
))}
diff --git
a/paimon-web-ui/src/pages/Playground/components/TabMenuSidebar/components/TreeComponent/tree-component.module.less
b/paimon-web-ui/src/pages/Playground/components/TabMenuSidebar/components/TreeComponent/tree-component.module.less
index 9c77a32..892457f 100644
---
a/paimon-web-ui/src/pages/Playground/components/TabMenuSidebar/components/TreeComponent/tree-component.module.less
+++
b/paimon-web-ui/src/pages/Playground/components/TabMenuSidebar/components/TreeComponent/tree-component.module.less
@@ -22,3 +22,7 @@ under the License. */
.catalog-tree-input {
background-color: var(--semi-color-bg-0);
}
+
+.selected-node {
+ background-color: #ff0000;
+}
diff --git a/paimon-web-ui/src/store/catalogStore.ts
b/paimon-web-ui/src/store/catalogStore.ts
new file mode 100644
index 0000000..a1efcf8
--- /dev/null
+++ b/paimon-web-ui/src/store/catalogStore.ts
@@ -0,0 +1,94 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import {create} from 'zustand';
+import {persist} from "zustand/middleware";
+import {CatalogItemList} from "@src/types/Catalog/data";
+import Api from "@api/api.ts";
+import {Toast} from "@douyinfe/semi-ui";
+
+type Store = {
+ catalogItemList: CatalogItemList;
+ createFileSystemCatalog: (catalogProp: Prop.CatalogProp) => Promise<void>;
+ createHiveCatalog: (catalogProp: Prop.CatalogProp) => Promise<void>;
+ fetchCatalogData: () => Promise<void>;
+};
+
+export const useCatalogStore = create<Store>()(persist(
+ (set) => ({
+ catalogItemList: [],
+ createFileSystemCatalog: async (catalogProp) => {
+ try {
+ const response = await
Api.createFileSystemCatalog(catalogProp);
+ if (!response) {
+ throw new Error('No response from
createFileSystemCatalog');
+ }
+ if (response.code === 200) {
+ Toast.success('Catalog created successfully!');
+ } else {
+ console.error('Failed to create catalog:', response.msg);
+ Toast.error('Failed to create catalog:' + response.msg);
+ }
+ } catch (error) {
+ console.error('Failed to create catalog:', error);
+ Toast.error('Failed to create catalog:' + error);
+ }
+ },
+ createHiveCatalog: async (catalogProp) => {
+ try {
+ const response = await Api.createHiveCatalog(catalogProp);
+ if (!response) {
+ throw new Error('No response from
createFileSystemCatalog');
+ }
+ if (response.code === 200) {
+ Toast.success('Catalog created successfully!');
+ } else {
+ console.error('Failed to create catalog:', response.msg);
+ Toast.error('Failed to create catalog:' + response.msg);
+ }
+ } catch (error) {
+ console.error('Failed to create catalog:', error);
+ Toast.error('Failed to create catalog:' + error);
+ }
+ },
+ fetchCatalogData: async () => {
+ try {
+ const result = await Api.getAllCatalogs();
+ if (result && result.data) {
+ const newCatalogItemList = result.data.map((item) => {
+ return {
+ id: item.id,
+ catalogName: item.catalogName,
+ catalogType: item.catalogType,
+ warehouse: item.warehouse,
+ hiveUri: item.hiveUri,
+ hiveConfDir: item.hiveConfDir,
+ isDelete: item.isDelete,
+ createTime: item.createTime,
+ updateTime: item.updateTime
+ };
+ });
+ set((state) => ({ ...state, catalogItemList:
newCatalogItemList }));
+ }
+ } catch (error) {
+ console.error('Failed to get catalogs:', error);
+ }
+ },
+ }),{
+ name: 'catalog-storage'
+ }
+))
\ No newline at end of file
diff --git
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/left-content.module.less
b/paimon-web-ui/src/types/Catalog/data.d.ts
similarity index 73%
copy from
paimon-web-ui/src/pages/Metadata/components/LeftContent/left-content.module.less
copy to paimon-web-ui/src/types/Catalog/data.d.ts
index bcee825..2bf4e51 100644
---
a/paimon-web-ui/src/pages/Metadata/components/LeftContent/left-content.module.less
+++ b/paimon-web-ui/src/types/Catalog/data.d.ts
@@ -15,17 +15,16 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-.container {
- width: 347px;
- height: 100%;
- padding: 10px 24px;
+export type CatalogItem = {
+ id: number,
+ catalogName: string,
+ catalogType: string,
+ warehouse: string,
+ hiveUri: string,
+ hiveConfDir: string,
+ isDelete: boolean,
+ createTime: Date,
+ updateTime: Date
}
-.add-catalog-container {
- color: var(--semi-color-text-0);
- margin: 6px 0 16px;
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- align-items: center;
-}
\ No newline at end of file
+export type CatalogItemList = CatalogItem[];
\ No newline at end of file
diff --git a/paimon-web-ui/tsconfig.json b/paimon-web-ui/tsconfig.json
index dc41217..eb79bd4 100644
--- a/paimon-web-ui/tsconfig.json
+++ b/paimon-web-ui/tsconfig.json
@@ -27,9 +27,10 @@
"@config/*": ["src/config/*"],
"@utils/*": ["src/utils/*"],
"@pages/*": ["src/pages/*"],
+ "@api/*": ["src/api/*"],
"@mock/*": ["mock/*"]
}
},
- "include": ["src"],
+ "include": ["src","./**/*.d.ts", "./**/*.ts", "./**/*.tsx"],
"references": [{ "path": "./tsconfig.node.json" }]
}
diff --git a/paimon-web-ui/vite.config.ts b/paimon-web-ui/vite.config.ts
index 812d23e..1a9f901 100644
--- a/paimon-web-ui/vite.config.ts
+++ b/paimon-web-ui/vite.config.ts
@@ -22,9 +22,9 @@ import { resolve } from 'path'
export default defineConfig({
plugins: [
- SemiPlugin({
+ /*SemiPlugin({
theme: "@semi-bot/semi-theme-figma"
- }),
+ }),*/
react()
],
resolve: {
@@ -35,7 +35,16 @@ export default defineConfig({
'@pages': resolve(__dirname, './src/pages'),
'@utils': resolve(__dirname, './src/utils'),
'@config': resolve(__dirname, './src/config'),
- '@mock': resolve(__dirname, './mock')
+ '@mock': resolve(__dirname, './mock'),
+ '@api': resolve(__dirname, './src/api')
+ }
+ },
+ server: {
+ proxy: {
+ '/api': {
+ target: 'http://localhost:10088',
+ changeOrigin: true
+ }
}
}
})
diff --git a/pom.xml b/pom.xml
index 1194f24..028091e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -435,6 +435,9 @@ under the License.
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>