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 2024/06/07 20:32:05 UTC

(camel-karavan) branch main updated: Fix #1312

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


The following commit(s) were added to refs/heads/main by this push:
     new 9e578b85 Fix #1312
9e578b85 is described below

commit 9e578b85cf425f27b2821e28c5ca8b16af4d8a32
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Fri Jun 7 16:31:58 2024 -0400

    Fix #1312
---
 .../org/apache/camel/karavan/KaravanCache.java     | 17 ++---
 .../camel/karavan/api/ProjectFileResource.java     |  8 +--
 karavan-app/src/main/webui/src/api/KaravanApi.tsx  |  4 +-
 karavan-app/src/main/webui/src/api/ProjectStore.ts |  6 +-
 .../main/webui/src/project/files/DiffFileModal.css | 20 ++++++
 .../main/webui/src/project/files/DiffFileModal.tsx | 81 ++++++++++++++++++++++
 .../src/main/webui/src/project/files/FilesTab.tsx  | 35 ++++++----
 .../main/webui/src/project/files/FilesToolbar.tsx  |  8 +--
 8 files changed, 144 insertions(+), 35 deletions(-)

diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/KaravanCache.java b/karavan-app/src/main/java/org/apache/camel/karavan/KaravanCache.java
index 387378d1..fae5d6d8 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/KaravanCache.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/KaravanCache.java
@@ -56,6 +56,12 @@ public class KaravanCache {
         return copy;
     }
 
+    private List<ProjectFile> getCopyProjectFilesCommited() {
+        List<ProjectFile> copy = new ArrayList<>(filesCommited.size());
+        filesCommited.values().forEach(e -> copy.add(e.copy()));
+        return copy;
+    }
+
     public List<CamelStatus> getCopyCamelStatuses() {
         List<CamelStatus> copy = new ArrayList<>(camelStatuses.size());
         camelStatuses.values().forEach(e -> copy.add(e.copy()));
@@ -111,7 +117,7 @@ public class KaravanCache {
 
     public void syncFilesCommited(String projectId) {
         List<String> currentFileNames = new ArrayList<>();
-        filesCommited.values().stream()
+        getCopyProjectFilesCommited().stream()
                 .filter(pf -> Objects.equals(pf.getProjectId(), projectId))
                 .forEach(pf -> currentFileNames.add(pf.getName()));
 
@@ -132,16 +138,11 @@ public class KaravanCache {
     }
 
     public List<ProjectFile> getProjectFilesCommited(String projectId) {
-        return filesCommited.values().stream().filter(pf -> Objects.equals(pf.getProjectId(), projectId)).toList();
-    }
-
-    public Map<String, ProjectFile> getProjectFilesCommitedMap(String projectId) {
-        return filesCommited.entrySet().stream().filter(es -> !Objects.isNull(es.getValue()) && Objects.equals(es.getValue().getProjectId(), projectId))
-                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+        return getCopyProjectFilesCommited().stream().filter(pf -> Objects.equals(pf.getProjectId(), projectId)).toList();
     }
 
     public ProjectFile getProjectFileCommited(String projectId, String filename) {
-        List<ProjectFile> list = filesCommited.values().stream().filter(pf -> Objects.equals(pf.getProjectId(), projectId) && Objects.equals(pf.getName(), filename)).toList();
+        List<ProjectFile> list = getCopyProjectFilesCommited().stream().filter(pf -> Objects.equals(pf.getProjectId(), projectId) && Objects.equals(pf.getName(), filename)).toList();
         return !list.isEmpty() ? list.get(0) : null;
     }
 
diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectFileResource.java b/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectFileResource.java
index ca2e64b7..f8a50656 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectFileResource.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectFileResource.java
@@ -51,11 +51,9 @@ public class ProjectFileResource {
 
     @GET
     @Produces(MediaType.APPLICATION_JSON)
-    @Path("/commited/{projectId}")
-    public List<ProjectFile> getCommited(@PathParam("projectId") String projectId) {
-        return karavanCache.getProjectFilesCommited(projectId).stream()
-                .sorted(Comparator.comparing(ProjectFile::getName))
-                .collect(Collectors.toList());
+    @Path("/commited/{projectId}/{filename}")
+    public ProjectFile getCommited(@PathParam("projectId") String projectId, @PathParam("filename") String filename) {
+        return karavanCache.getProjectFileCommited(projectId, filename);
     }
 
     @GET
diff --git a/karavan-app/src/main/webui/src/api/KaravanApi.tsx b/karavan-app/src/main/webui/src/api/KaravanApi.tsx
index dadce4db..4cf61d0d 100644
--- a/karavan-app/src/main/webui/src/api/KaravanApi.tsx
+++ b/karavan-app/src/main/webui/src/api/KaravanApi.tsx
@@ -349,8 +349,8 @@ export class KaravanApi {
         });
     }
 
-    static async getFilesCommited(projectId: string, after: (files: ProjectFile[]) => void) {
-        instance.get('/ui/file/commited/' + projectId)
+    static async getFileCommited(projectId: string, filename: string, after: (file: ProjectFile) => void) {
+        instance.get('/ui/file/commited/' + projectId + '/' + filename)
             .then(res => {
                 if (res.status === 200) {
                     after(res.data);
diff --git a/karavan-app/src/main/webui/src/api/ProjectStore.ts b/karavan-app/src/main/webui/src/api/ProjectStore.ts
index 5d5a9913..ad4ec8f5 100644
--- a/karavan-app/src/main/webui/src/api/ProjectStore.ts
+++ b/karavan-app/src/main/webui/src/api/ProjectStore.ts
@@ -217,9 +217,9 @@ export const useFilesStore = createWithEqualityFn<FilesState>((set) => ({
 
 interface FileState {
     file?: ProjectFile;
-    operation: "create" | "select" | "delete" | "none" | "copy" | "upload";
+    operation: "create" | "select" | "delete" | "none" | "copy" | "upload" | "diff";
     designerTab?: "routes" | "rest" | "beans" | "kamelet";
-    setFile: (operation:  "create" | "select" | "delete"| "none" | "copy" | "upload", file?: ProjectFile, designerTab?: "routes" | "rest" | "beans" | "kamelet") => void;
+    setFile: (operation:  "create" | "select" | "delete"| "none" | "copy" | "upload" | "diff", file?: ProjectFile, designerTab?: "routes" | "rest" | "beans" | "kamelet") => void;
 }
 
 export const useFileStore = createWithEqualityFn<FileState>((set) => ({
@@ -227,7 +227,7 @@ export const useFileStore = createWithEqualityFn<FileState>((set) => ({
     operation: "none",
     designerTab: undefined,
     addProperty: '',
-    setFile: (operation:  "create" | "select" | "delete"| "none" | "copy" | "upload", file?: ProjectFile, designerTab?: "routes" | "rest" | "beans" | "kamelet") => {
+    setFile: (operation:  "create" | "select" | "delete"| "none" | "copy" | "upload" | "diff", file?: ProjectFile, designerTab?: "routes" | "rest" | "beans" | "kamelet") => {
         set((state: FileState) => ({
             file: file,
             operation: operation,
diff --git a/karavan-app/src/main/webui/src/project/files/DiffFileModal.css b/karavan-app/src/main/webui/src/project/files/DiffFileModal.css
new file mode 100644
index 00000000..5a3cb86f
--- /dev/null
+++ b/karavan-app/src/main/webui/src/project/files/DiffFileModal.css
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+.karavan-diff-modal {
+    height: 90%;
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/project/files/DiffFileModal.tsx b/karavan-app/src/main/webui/src/project/files/DiffFileModal.tsx
new file mode 100644
index 00000000..d05856e6
--- /dev/null
+++ b/karavan-app/src/main/webui/src/project/files/DiffFileModal.tsx
@@ -0,0 +1,81 @@
+/*
+ * 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, {useEffect, useState} from 'react';
+import {
+    Button,
+    Modal,
+    ModalVariant, Spinner
+} from '@patternfly/react-core';
+import './DiffFileModal.css';
+import {useFileStore, useProjectStore} from "../../api/ProjectStore";
+import {DiffEditor} from "@monaco-editor/react";
+import {KaravanApi} from "../../api/KaravanApi";
+import {ProjectFile} from "../../api/ProjectModels";
+import {shallow} from "zustand/shallow";
+
+const languages = new Map<string, string>([
+    ['sh', 'shell'],
+    ['md', 'markdown'],
+    ['properties', 'ini'],
+    ['java', 'java'],
+    ['yaml', 'yaml'],
+    ['json', 'json'],
+    ['xml', 'xml']
+])
+
+interface Props {
+    id: string
+}
+
+export function DiffFileModal (prop: Props) {
+
+    const [project] = useProjectStore((s) => [s.project], shallow);
+    const {file, operation} = useFileStore();
+    const [fileCommited, setFileCommited] = useState<string>();
+
+    useEffect(() => {
+        if (file && operation === 'diff') {
+            KaravanApi.getFileCommited(project.projectId, file?.name, (fileCommited: ProjectFile) => {
+                setFileCommited(fileCommited.code);
+            });
+        }
+    }, [project, file, operation]);
+
+    function closeModal () {
+        useFileStore.setState({operation: "none"})
+        setFileCommited(undefined)
+    }
+
+    const isOpen= operation === "diff";
+    const extension = file?.name.split('.').pop();
+    const language = extension && languages.has(extension) ? languages.get(extension) : extension;
+    return (
+            <Modal
+                className='karavan-diff-modal'
+                title="Diff"
+                isOpen={isOpen}
+                onClose={() => closeModal()}
+                actions={[
+                    <Button key="confirm" variant="primary" onClick={e => closeModal()}>Close</Button>,
+                ]}
+                onEscapePress={e => closeModal()}>
+                {fileCommited === undefined && <Spinner size="lg" aria-label="spinner"/>}
+                {fileCommited !== undefined && <DiffEditor language={language} original={fileCommited} modified={file?.code}/>}
+            </Modal>
+    )
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/project/files/FilesTab.tsx b/karavan-app/src/main/webui/src/project/files/FilesTab.tsx
index 046c79d3..7fa9d4ad 100644
--- a/karavan-app/src/main/webui/src/project/files/FilesTab.tsx
+++ b/karavan-app/src/main/webui/src/project/files/FilesTab.tsx
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-import React from 'react';
+import React, {useState} from 'react';
 import {
     Badge,
     Bullseye,
@@ -44,27 +44,22 @@ import {
 } from "../../api/ProjectModels";
 import {FileToolbar} from "./FilesToolbar";
 import DownloadIcon from "@patternfly/react-icons/dist/esm/icons/download-icon";
+import DiffIcon from "@patternfly/react-icons/dist/esm/icons/outlined-copy-icon";
 import FileSaver from "file-saver";
 import {CreateFileModal} from "./CreateFileModal";
 import {DeleteFileModal} from "./DeleteFileModal";
 import {UploadFileModal} from "./UploadFileModal";
 import {shallow} from "zustand/shallow";
 import {CreateIntegrationModal} from "./CreateIntegrationModal";
+import {DiffFileModal} from "./DiffFileModal";
 
 export function FilesTab () {
 
     const [files, diff] = useFilesStore((s) => [s.files, s.diff], shallow);
     const [project] = useProjectStore((s) => [s.project], shallow);
     const [setFile] = useFileStore((s) => [s.setFile], shallow);
+    const [id, setId] = useState<string>('');
 
-    function getDate(lastUpdate: number): string {
-        if (lastUpdate) {
-            const date = new Date(lastUpdate);
-            return date.toISOString().slice(0, 19).replace('T',' ');
-        } else {
-            return "N/A"
-        }
-    }
 
     function needCommit(filename: string): boolean {
         return diff && diff[filename] !== undefined;
@@ -93,6 +88,7 @@ export function FilesTab () {
                     <FileToolbar/>
                 </PanelHeader>
             </Panel>
+            <DiffFileModal id={id}/>
             <div style={{height:"100%", overflow:"auto"}}>
                 <Table aria-label="Files" variant={"compact"} className={"table"}>
                     <Thead>
@@ -106,6 +102,7 @@ export function FilesTab () {
                     <Tbody>
                         {files.map(file => {
                             const type = getProjectFileTypeTitle(file)
+                            const diffType = diff[file.name];
                             return <Tr key={file.name}>
                                 <Td>
                                     <Badge>{type}</Badge>
@@ -119,8 +116,22 @@ export function FilesTab () {
                                     </Button>
                                 </Td>
                                 <Td>
-                                    {needCommit(file.name) &&
-                                        <Label color="grey">{diff[file.name]}</Label>
+                                    {needCommit(file.name) && diffType === 'CHANGED' &&
+                                        <Tooltip content="Show diff" position={"right"}>
+                                            <Label color="grey">
+                                                <Button size="sm" variant="link" className='labeled-button'
+                                                        icon={<DiffIcon/>}
+                                                        onClick={e => {
+                                                            setFile('diff', file, undefined);
+                                                            setId(Math.random().toString());
+                                                        }}>
+                                                    {diffType}
+                                                </Button>
+                                            </Label>
+                                        </Tooltip>
+                                    }
+                                    {needCommit(file.name) && diffType !== 'CHANGED' &&
+                                        <Label color="grey">{diffType}</Label>
                                     }
                                     {!needCommit(file.name) &&
                                         <Label color="green" icon={<CheckIcon/>}/>
@@ -142,7 +153,7 @@ export function FilesTab () {
                                 </Td>
                             </Tr>
                         })}
-                        {diff && Object.keys(diff).map(fileName => {
+                        {diff && Object.keys(diff).filter(f => diff[f] === 'DELETE').map(fileName => {
                             const type = getProjectFileTypeByNameTitle(fileName)
                             return <Tr key={fileName}>
                                 <Td><Badge>{type}</Badge></Td>
diff --git a/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx b/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx
index 6f0859bf..d2125d32 100644
--- a/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx
+++ b/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx
@@ -44,11 +44,9 @@ export function FileToolbar () {
     const [commitMessageIsOpen, setCommitMessageIsOpen] = useState(false);
     const [pullIsOpen, setPullIsOpen] = useState(false);
     const [commitMessage, setCommitMessage] = useState('');
-    const [project, isPushing, isPulling] =
-        useProjectStore((s) => [s.project, s.isPushing, s.isPulling], shallow )
-    const [files, diff] = useFilesStore((s) => [s.files, s.diff], shallow);
-    const [file, setFile] = useFileStore((s) =>
-        [s.file, s.setFile], shallow )
+    const [project, isPushing, isPulling] = useProjectStore((s) => [s.project, s.isPushing, s.isPulling], shallow )
+    const [diff] = useFilesStore((s) => [s.diff], shallow);
+    const [file, setFile] = useFileStore((s) => [s.file, s.setFile], shallow )
 
     useEffect(() => {
     }, [project, file]);