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]);