You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2022/11/11 14:05:47 UTC

[camel-karavan] 04/04: Templates page

This is an automated email from the ASF dual-hosted git repository.

marat pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karavan.git

commit d9296297387251bd6387ebcb7bb1aeb04972b33f
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Fri Nov 11 09:05:28 2022 -0500

    Templates page
---
 .../apache/camel/karavan/api/ProjectResource.java  |  3 +-
 .../apache/camel/karavan/api/TemplateResource.java | 60 ++++----------------
 karavan-app/src/main/webapp/src/Main.tsx           | 14 +++--
 karavan-app/src/main/webapp/src/api/KaravanApi.tsx | 22 ++++++++
 .../main/webapp/src/config/ConfigurationPage.tsx   | 66 +++++++++++++++++-----
 .../main/webapp/src/dashboard/DashboardPage.tsx    |  2 +-
 .../src/main/webapp/src/projects/ProjectInfo.tsx   |  1 -
 .../src/main/webapp/src/projects/ProjectPage.tsx   | 66 ++++++++++++++++------
 .../src/main/webapp/src/projects/ProjectsPage.tsx  |  2 +-
 9 files changed, 149 insertions(+), 87 deletions(-)

diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java b/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java
index 85a981f..b2e12b6 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java
@@ -48,6 +48,8 @@ public class ProjectResource {
     @Produces(MediaType.APPLICATION_JSON)
     public List<Project> getAll() throws Exception {
         return infinispanService.getProjects().stream()
+                .filter(project -> !project.getName().equalsIgnoreCase(Project.NAME_TEMPLATES))
+                .filter(project -> !project.getName().equalsIgnoreCase(Project.NAME_KAMELETS))
                 .sorted(Comparator.comparing(Project::getProjectId))
                 .collect(Collectors.toList());
     }
@@ -86,7 +88,6 @@ public class ProjectResource {
         Project s = infinispanService.getProject(sourceProject);
         project.setRuntime(s.getRuntime());
         infinispanService.saveProject(project, false);
-
 //        Copy files
         Map<GroupedKey, ProjectFile> map = infinispanService.getProjectFiles(sourceProject).stream()
                 .collect(Collectors.toMap(f -> new GroupedKey(project.getProjectId(), f.getName()), f -> f));
diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/api/TemplateResource.java b/karavan-app/src/main/java/org/apache/camel/karavan/api/TemplateResource.java
index b2e12b6..2be7c7e 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/api/TemplateResource.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/api/TemplateResource.java
@@ -16,82 +16,46 @@
  */
 package org.apache.camel.karavan.api;
 
-import org.apache.camel.karavan.model.GroupedKey;
 import org.apache.camel.karavan.model.Project;
 import org.apache.camel.karavan.model.ProjectFile;
 import org.apache.camel.karavan.service.InfinispanService;
 
 import javax.inject.Inject;
 import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
-import javax.ws.rs.HeaderParam;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
-import java.net.URLDecoder;
-import java.nio.charset.StandardCharsets;
-import java.util.Comparator;
 import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
 
-@Path("/api/project")
-public class ProjectResource {
+@Path("/api/template")
+public class TemplateResource {
 
     @Inject
     InfinispanService infinispanService;
 
     @GET
     @Produces(MediaType.APPLICATION_JSON)
-    public List<Project> getAll() throws Exception {
-        return infinispanService.getProjects().stream()
-                .filter(project -> !project.getName().equalsIgnoreCase(Project.NAME_TEMPLATES))
-                .filter(project -> !project.getName().equalsIgnoreCase(Project.NAME_KAMELETS))
-                .sorted(Comparator.comparing(Project::getProjectId))
-                .collect(Collectors.toList());
+    public Project get() throws Exception {
+        return infinispanService.getProject(Project.NAME_TEMPLATES);
     }
 
     @GET
     @Produces(MediaType.APPLICATION_JSON)
-    @Path("/{project}")
-    public Project get(@PathParam("project") String project) throws Exception {
-        return infinispanService.getProject(project);
+    @Path("/files")
+    public List<ProjectFile> getFiles() throws Exception {
+        return infinispanService.getProjectFiles(Project.NAME_TEMPLATES);
     }
 
     @POST
     @Produces(MediaType.APPLICATION_JSON)
     @Consumes(MediaType.APPLICATION_JSON)
-    public Project save(Project project) throws Exception {
-        infinispanService.saveProject(project, false);
-        return project;
-    }
-
-    @DELETE
-    @Produces(MediaType.APPLICATION_JSON)
-    @Path("/{project}")
-    public void delete(@HeaderParam("username") String username,
-                          @PathParam("project") String project) throws Exception {
-        String projectId = URLDecoder.decode(project, StandardCharsets.UTF_8.toString());
-        infinispanService.getProjectFiles(projectId).forEach(file -> infinispanService.deleteProjectFile(projectId, file.getName()));
-        infinispanService.deleteProject(projectId);
-    }
-
-    @POST
-    @Produces(MediaType.APPLICATION_JSON)
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Path("/copy/{sourceProject}")
-    public Project copy(@PathParam("sourceProject") String sourceProject, Project project) throws Exception {
-//        Save project
-        Project s = infinispanService.getProject(sourceProject);
-        project.setRuntime(s.getRuntime());
-        infinispanService.saveProject(project, false);
-//        Copy files
-        Map<GroupedKey, ProjectFile> map = infinispanService.getProjectFiles(sourceProject).stream()
-                .collect(Collectors.toMap(f -> new GroupedKey(project.getProjectId(), f.getName()), f -> f));
-        infinispanService.saveProjectFiles(map);
-        return project;
+    public ProjectFile save(ProjectFile file) throws Exception {
+        if (file.getProjectId().equalsIgnoreCase(Project.NAME_TEMPLATES)){
+            infinispanService.saveProjectFile(file);
+        }
+        return file;
     }
 }
\ No newline at end of file
diff --git a/karavan-app/src/main/webapp/src/Main.tsx b/karavan-app/src/main/webapp/src/Main.tsx
index ccb825e..f3dcdc3 100644
--- a/karavan-app/src/main/webapp/src/Main.tsx
+++ b/karavan-app/src/main/webapp/src/Main.tsx
@@ -13,7 +13,6 @@ import {KaravanApi} from "./api/KaravanApi";
 import {SsoApi} from "./api/SsoApi";
 import {KameletApi} from "karavan-core/lib/api/KameletApi";
 import './designer/karavan.css';
-import {ConfigurationPage} from "./config/ConfigurationPage";
 import {KameletsPage} from "./kamelets/KameletsPage";
 import {v4 as uuidv4} from "uuid";
 import {ComponentApi} from "karavan-core/lib/api/ComponentApi";
@@ -29,7 +28,7 @@ import KameletsIcon from "@patternfly/react-icons/dist/js/icons/registry-icon";
 import DashboardIcon from "@patternfly/react-icons/dist/js/icons/tachometer-alt-icon";
 import EipIcon from "@patternfly/react-icons/dist/js/icons/topology-icon";
 import ComponentsIcon from "@patternfly/react-icons/dist/js/icons/module-icon";
-import ConfigurationIcon from "@patternfly/react-icons/dist/js/icons/cogs-icon";
+import TemplatesIcon from "@patternfly/react-icons/dist/js/icons/cogs-icon";
 import {MainLogin} from "./MainLogin";
 import {DashboardPage} from "./dashboard/DashboardPage";
 
@@ -67,6 +66,7 @@ interface State {
     pageId: string,
     projects: Project[],
     project?: Project,
+    templates?: Project,
     isModalOpen: boolean,
     projectToDelete?: Project,
     openapi: string,
@@ -125,6 +125,9 @@ export class Main extends React.Component<Props, State> {
     getData() {
         KaravanApi.getConfiguration((config: any) => {
             this.setState({config: config, request: uuidv4()});
+            KaravanApi.getTemplatesProject((templates: Project) => {
+                this.setState({templates: templates});
+            });
         });
         this.updateKamelets();
         this.updateComponents();
@@ -162,7 +165,7 @@ export class Main extends React.Component<Props, State> {
             new MenuItem("eip", "Enterprise Integration Patterns", <EipIcon/>),
             new MenuItem("kamelets", "Kamelets", <KameletsIcon/>),
             new MenuItem("components", "Components", <ComponentsIcon/>),
-            new MenuItem("configuration", "Configuration", <ConfigurationIcon/>)
+            new MenuItem("templates", "Templates", <TemplatesIcon/>)
         ]
         return (<Flex className="nav-buttons" direction={{default: "column"}} style={{height: "100%"}}
                       spaceItems={{default: "spaceItemsNone"}}>
@@ -235,12 +238,13 @@ export class Main extends React.Component<Props, State> {
                                           toast={this.toast}
                                           config={this.state.config}/>}
                         {this.state.pageId === 'project' && this.state.project &&
-                            <ProjectPage project={this.state.project} config={this.state.config}/>}
+                            <ProjectPage key="projects" project={this.state.project} config={this.state.config}/>}
                         {this.state.pageId === 'dashboard' && <DashboardPage key={this.state.request}
                                                                              onSelect={this.onProjectSelect}
                                                                              toast={this.toast}
                                                                              config={this.state.config}/>}
-                        {this.state.pageId === 'configuration' && <ConfigurationPage/>}
+                        {this.state.pageId === 'templates' && this.state.templates &&
+                            <ProjectPage key="templates" project={this.state.templates} isTemplates={true} config={this.state.config}/>}
                         {this.state.pageId === 'kamelets' &&
                             <KameletsPage dark={false} onRefresh={this.updateKamelets}/>}
                         {this.state.pageId === 'components' &&
diff --git a/karavan-app/src/main/webapp/src/api/KaravanApi.tsx b/karavan-app/src/main/webapp/src/api/KaravanApi.tsx
index 46151c2..e6e2003 100644
--- a/karavan-app/src/main/webapp/src/api/KaravanApi.tsx
+++ b/karavan-app/src/main/webapp/src/api/KaravanApi.tsx
@@ -277,6 +277,28 @@ export class KaravanApi {
         });
     }
 
+    static async getTemplatesProject( after: (project: Project) => void) {
+        instance.get('/api/template')
+            .then(res => {
+                if (res.status === 200) {
+                    after(res.data);
+                }
+            }).catch(err => {
+            console.log(err);
+        });
+    }
+
+    static async getTemplatesFiles( after: (files: []) => void) {
+        instance.get('/api/template/files')
+            .then(res => {
+                if (res.status === 200) {
+                    after(res.data);
+                }
+            }).catch(err => {
+            console.log(err);
+        });
+    }
+
     static async pipelineRun(project: Project, environment: string, after: (res: AxiosResponse<any>) => void) {
         instance.post('/api/kubernetes/pipeline/' + environment, project)
             .then(res => {
diff --git a/karavan-app/src/main/webapp/src/config/ConfigurationPage.tsx b/karavan-app/src/main/webapp/src/config/ConfigurationPage.tsx
index 1a88233..c9efa9f 100644
--- a/karavan-app/src/main/webapp/src/config/ConfigurationPage.tsx
+++ b/karavan-app/src/main/webapp/src/config/ConfigurationPage.tsx
@@ -1,39 +1,77 @@
 import React from 'react';
 import {
-    Gallery,
-    PageSection
+    Badge, Bullseye, Button, EmptyState, EmptyStateIcon, EmptyStateVariant, Flex, FlexItem,
+    Gallery, OverflowMenu, OverflowMenuContent, OverflowMenuGroup, OverflowMenuItem,
+    PageSection, Tab, Tabs, TabTitleText, Text, TextContent, TextInput, Title, Toolbar, ToolbarContent, ToolbarItem, Tooltip
 } from '@patternfly/react-core';
 import '../designer/karavan.css';
+import {MainToolbar} from "../MainToolbar";
+import {TableComposable, Tbody, Td, Th, Thead, Tr} from "@patternfly/react-table";
+import CopyIcon from "@patternfly/react-icons/dist/esm/icons/copy-icon";
+import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon";
+import SearchIcon from "@patternfly/react-icons/dist/esm/icons/search-icon";
+import RefreshIcon from "@patternfly/react-icons/dist/esm/icons/sync-alt-icon";
+import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
+import {KaravanApi} from "../api/KaravanApi";
+import {DeploymentStatus, Project} from "../projects/ProjectModels";
 
 interface Props {
 }
 
 interface State {
-    repository: string,
-    path: string,
-    integrations: []
+    templates: [],
 }
 
 export class ConfigurationPage extends React.Component<Props, State> {
 
     public state: State = {
-        repository: '',
-        path: '',
-        integrations: []
+        templates: []
     };
 
     componentDidMount() {
+        this.onGetTemplates();
+    }
 
+    onGetTemplates = () => {
+        // KaravanApi.getTemplates((templates: []) => {
+        //     console.log(templates)
+        //     this.setState({templates: templates})
+        // });
     }
 
+    tools = () => (<Toolbar id="toolbar-group-types">
+        <ToolbarContent>
+            <ToolbarItem>
+                <Button variant="link" icon={<RefreshIcon/>} onClick={e => this.onGetTemplates()}/>
+            </ToolbarItem>
+        </ToolbarContent>
+    </Toolbar>);
+
+    title = () => (<TextContent>
+        <Text component="h2">Configuration</Text>
+    </TextContent>);
+
     render() {
         return (
-            <PageSection  padding={{ default: 'noPadding' }}>
-                <Gallery hasGutter>
-                    {/*{this.state.integrations.map(value => (*/}
-                    {/*    // <IntegrationCard name={value}/>*/}
-                    {/*))}*/}
-                </Gallery>
+            <PageSection className="kamelet-section projects-page" padding={{default: 'noPadding'}}>
+                <PageSection className="tools-section" padding={{default: 'noPadding'}}>
+                    <MainToolbar title={this.title()} tools={this.tools()}/>
+                </PageSection>
+                <PageSection isFilled className="kamelets-page">
+                    <Tabs
+                        // activeKey={activeTabKey}
+                        // onSelect={handleTabClick}
+                        aria-label="Configurations"
+                        role="tabs"
+                    >
+                        <Tab eventKey={0} title={<TabTitleText>Templates</TabTitleText>} aria-label="Templates">
+                            Templates
+                        </Tab>
+                        <Tab eventKey={11} title={<TabTitleText>Environments</TabTitleText>}>
+                            Environments
+                        </Tab>
+                    </Tabs>
+                </PageSection>
             </PageSection>
         );
     }
diff --git a/karavan-app/src/main/webapp/src/dashboard/DashboardPage.tsx b/karavan-app/src/main/webapp/src/dashboard/DashboardPage.tsx
index fbff4d5..8f5fe20 100644
--- a/karavan-app/src/main/webapp/src/dashboard/DashboardPage.tsx
+++ b/karavan-app/src/main/webapp/src/dashboard/DashboardPage.tsx
@@ -135,7 +135,7 @@ export class DashboardPage extends React.Component<Props, State> {
     </Toolbar>);
 
     title = () => (<TextContent>
-        <Text component="h1">Dashboard</Text>
+        <Text component="h2">Dashboard</Text>
     </TextContent>);
 
     getEnvironments(): string [] {
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx b/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx
index 10fd3df..f8525e5 100644
--- a/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx
+++ b/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx
@@ -330,7 +330,6 @@ export class ProjectInfo extends React.Component<Props, State> {
     }
 
     getPipelineState(env: string) {
-        // console.log(this.state.pipelineStatus)
         const status = this.state.pipelineStatus;
         const pipeline = status?.pipelineName;
         const pipelineResult = status?.result;
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx b/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx
index f53057c..9150d7b 100644
--- a/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx
+++ b/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx
@@ -21,7 +21,7 @@ import {
     ToggleGroup,
     ToggleGroupItem,
     CodeBlockCode,
-    CodeBlock, Skeleton, Checkbox, Tabs, Tab, Tooltip
+    CodeBlock, Skeleton, Checkbox, Tabs, Tab, Tooltip, ToolbarItem
 } from '@patternfly/react-core';
 import '../designer/karavan.css';
 import {MainToolbar} from "../MainToolbar";
@@ -47,9 +47,12 @@ import {UploadModal} from "./UploadModal";
 import {ProjectInfo} from "./ProjectInfo";
 import {ProjectOperations} from "./ProjectOperations";
 import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
+import RefreshIcon from "@patternfly/react-icons/dist/esm/icons/sync-alt-icon";
+import PushIcon from "@patternfly/react-icons/dist/esm/icons/code-branch-icon";
 
 interface Props {
     project: Project,
+    isTemplates?: boolean,
     config: any,
 }
 
@@ -165,6 +168,7 @@ export class ProjectPage extends React.Component<Props, State> {
     }
 
     tools = () => {
+        const {isTemplates, project} = this.props;
         const {file, mode} = this.state;
         const isFile = file !== undefined;
         const isYaml = file !== undefined && file.name.endsWith("yaml");
@@ -172,6 +176,30 @@ export class ProjectPage extends React.Component<Props, State> {
         const isProperties = file !== undefined && file.name.endsWith("properties");
         return <Toolbar id="toolbar-group-types">
             <ToolbarContent>
+                {isTemplates &&
+                    <ToolbarItem>
+                        <Flex justifyContent={{default: "justifyContentSpaceBetween"}} alignItems={{default: "alignItemsCenter"}}>
+                            <FlexItem>
+                                <Tooltip content={project?.lastCommit} position={"right"}>
+                                    <Badge>{project?.lastCommit ? project?.lastCommit?.substr(0, 7) : "-"}</Badge>
+                                </Tooltip>
+                            </FlexItem>
+                            <FlexItem>
+                                <Button variant="primary" icon={<PushIcon/>} onClick={e => {
+                                    KaravanApi.push(this.props.project, res => {
+                                        console.log(res)
+                                        if (res.status === 200 || res.status === 201) {
+                                            this.onRefresh();
+                                        } else {
+                                            // Todo notification
+                                        }
+                                    });
+                                }}>Commit</Button>
+                            </FlexItem>
+                        </Flex>
+                    </ToolbarItem>
+                }
+                {!isTemplates &&
                 <Flex className="toolbar" direction={{default: "row"}} alignItems={{default:"alignItemsCenter"}}>
                     {isYaml && <FlexItem>
                         <ToggleGroup>
@@ -210,7 +238,7 @@ export class ProjectPage extends React.Component<Props, State> {
                         <Button variant="secondary" icon={<UploadIcon/>}
                                 onClick={e => this.setState({isUploadModalOpen: true})}>Upload</Button>
                     </FlexItem>}
-                </Flex>
+                </Flex>}
             </ToolbarContent>
         </Toolbar>
     }
@@ -233,6 +261,7 @@ export class ProjectPage extends React.Component<Props, State> {
     }
 
     title = () => {
+        const {project, isTemplates} = this.props;
         const file = this.state.file;
         const isFile = file !== undefined;
         const isLog = file !== undefined && file.name.endsWith("log");
@@ -242,7 +271,7 @@ export class ProjectPage extends React.Component<Props, State> {
                 <div>
                     <Breadcrumb>
                         <BreadcrumbItem to="#" onClick={event => this.setState({file: undefined})}>
-                            {"Project: " + this.props.project?.projectId}
+                            {"Project: " + project?.projectId}
                         </BreadcrumbItem>
                         <BreadcrumbItem to="#" isActive>{this.getType(file)}</BreadcrumbItem>
                     </Breadcrumb>
@@ -252,7 +281,7 @@ export class ProjectPage extends React.Component<Props, State> {
                 </div>
             }
             {!isFile && <TextContent className="title">
-                <Text component="h1">Project</Text>
+                <Text component="h2">{isTemplates ? 'Templates' : 'Project: ' + project?.projectId}</Text>
             </TextContent>}
         </div>)
     };
@@ -445,6 +474,7 @@ export class ProjectPage extends React.Component<Props, State> {
     }
 
     render() {
+        const {isTemplates} = this.props;
         const {file, mode, tab} = this.state;
         const isYaml = file !== undefined && file.name.endsWith("yaml");
         const isIntegration = isYaml && file?.code && CamelDefinitionYaml.yamlIsIntegration(file.code);
@@ -462,18 +492,22 @@ export class ProjectPage extends React.Component<Props, State> {
                     <PageSection isFilled className="kamelets-page project-page-section"
                                  padding={{default: file !== undefined ? 'noPadding' : 'noPadding'}}>
                         <Flex direction={{default: "column"}} spaceItems={{default: "spaceItemsNone"}}>
-                            <FlexItem>
-                                <Tabs activeKey={tab} onSelect={(event, tabIndex) => this.setState({tab: tabIndex})}>
-                                    <Tab eventKey="development" title="Development"/>
-                                    <Tab eventKey="operations" title="Operations"/>
-                                </Tabs>
-                            </FlexItem>
-                            <FlexItem>
-                                <PageSection padding={{default: "padding"}}>
-                                    {tab === 'development' && <ProjectInfo project={this.props.project} config={this.props.config} deleteEntity={this.deleteEntity} showLog={this.showLogs}/>}
-                                    {tab === 'operations' && <ProjectOperations environments={this.state.environments} project={this.props.project} config={this.props.config}/>}
-                                </PageSection>
-                            </FlexItem>
+                            {!isTemplates &&
+                                <FlexItem>
+                                    <Tabs activeKey={tab} onSelect={(event, tabIndex) => this.setState({tab: tabIndex})}>
+                                        <Tab eventKey="development" title="Development"/>
+                                        <Tab eventKey="operations" title="Operations"/>
+                                    </Tabs>
+                                </FlexItem>
+                            }
+                            {!isTemplates &&
+                                <FlexItem>
+                                    <PageSection padding={{default: "padding"}}>
+                                        {tab === 'development' && <ProjectInfo project={this.props.project} config={this.props.config} deleteEntity={this.deleteEntity} showLog={this.showLogs}/>}
+                                        {tab === 'operations' && <ProjectOperations environments={this.state.environments} project={this.props.project} config={this.props.config}/>}
+                                    </PageSection>
+                                </FlexItem>
+                            }
                         </Flex>
                         {tab === 'development' && this.getProjectFiles()}
                     </PageSection>}
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx b/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx
index 15676f6..08bb7c7 100644
--- a/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx
+++ b/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx
@@ -140,7 +140,7 @@ export class ProjectsPage extends React.Component<Props, State> {
     </Toolbar>);
 
     title = () => (<TextContent>
-        <Text component="h1">Projects</Text>
+        <Text component="h2">Projects</Text>
     </TextContent>);
 
     closeModal = () => {