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/01/31 22:02:28 UTC
(camel-karavan) 04/04: #1091 in App
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 7efc3af456fa0ecb65584a990b969a5e2b5f9363
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Wed Jan 31 16:57:44 2024 -0500
#1091 in App
---
.../src/main/webui/src/designer/DesignerStore.ts | 16 ++++-
.../main/webui/src/designer/KaravanDesigner.tsx | 8 ++-
.../webui/src/designer/property/DslProperties.css | 7 +-
.../webui/src/designer/property/DslProperties.tsx | 10 ++-
.../property/property/ComponentPropertyField.tsx | 49 +++++++++----
.../ComponentPropertyPlaceholderDropdown.css | 24 +++++++
.../ComponentPropertyPlaceholderDropdown.tsx | 81 ++++++++++++++++++++++
.../property/property/DslPropertyField.tsx | 1 +
.../src/main/webui/src/project/FileEditor.tsx | 18 ++++-
.../src/main/webui/src/util/StringUtils.ts | 21 ++++++
10 files changed, 205 insertions(+), 30 deletions(-)
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/DesignerStore.ts b/karavan-web/karavan-app/src/main/webui/src/designer/DesignerStore.ts
index 407713a7..fe198a7f 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/DesignerStore.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/DesignerStore.ts
@@ -168,7 +168,8 @@ type DesignerState = {
height: number,
top: number,
left: number,
- moveElements: [string | undefined, string | undefined]
+ moveElements: [string | undefined, string | undefined],
+ propertyPlaceholders: string[];
}
const designerState: DesignerState = {
@@ -186,7 +187,8 @@ const designerState: DesignerState = {
height: 0,
top: 0,
left: 0,
- moveElements: [undefined, undefined]
+ moveElements: [undefined, undefined],
+ propertyPlaceholders: []
};
type DesignerAction = {
@@ -203,6 +205,7 @@ type DesignerAction = {
reset: () => void;
setNotification: (notificationBadge: boolean, notificationMessage: [string, string]) => void;
setMoveElements: (moveElements: [string | undefined, string | undefined]) => void;
+ setPropertyPlaceholders: (propertyPlaceholders: string[]) => void;
}
export const useDesignerStore = createWithEqualityFn<DesignerState & DesignerAction>((set) => ({
@@ -257,5 +260,12 @@ export const useDesignerStore = createWithEqualityFn<DesignerState & DesignerAct
},
setMoveElements: (moveElements: [string | undefined, string | undefined]) => {
set({moveElements: moveElements})
- }
+ },
+ setPropertyPlaceholders: (propertyPlaceholders: string[]) => {
+ set((state: DesignerState) => {
+ state.propertyPlaceholders.length = 0;
+ state.propertyPlaceholders.push(...propertyPlaceholders);
+ return state;
+ })
+ },
}), shallow)
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx
index 626f984b..349ae548 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx
@@ -41,7 +41,6 @@ import {BeansDesigner} from "./beans/BeansDesigner";
import {CodeEditor} from "./editor/CodeEditor";
import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';
import {KameletDesigner} from "./kamelet/KameletDesigner";
-import {v4 as uuidv4} from "uuid";
interface Props {
onSave: (filename: string, yaml: string, propertyOnly: boolean) => void
@@ -53,13 +52,15 @@ interface Props {
hideLogDSL?: boolean
showCodeTab: boolean
tab?: "routes" | "rest" | "beans"
+ propertyPlaceholders: string[]
}
export function KaravanDesigner(props: Props) {
const [tab, setTab] = useState<string>('routes');
- const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset, badge, message] = useDesignerStore((s) =>
- [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset, s.notificationBadge, s.notificationMessage], shallow)
+ const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset, badge, message, setPropertyPlaceholders] =
+ useDesignerStore((s) =>
+ [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset, s.notificationBadge, s.notificationMessage, s.setPropertyPlaceholders], shallow)
const [integration, setIntegration] = useIntegrationStore((s) =>
[s.integration, s.setIntegration], shallow)
@@ -83,6 +84,7 @@ export function KaravanDesigner(props: Props) {
setTab(designerTab || 'routes')
reset();
setDark(props.dark);
+ setPropertyPlaceholders(props.propertyPlaceholders)
setHideLogDSL(props.hideLogDSL === true);
return () => {
sub?.unsubscribe();
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/property/DslProperties.css b/karavan-web/karavan-app/src/main/webui/src/designer/property/DslProperties.css
index dd7cb199..43562bcb 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/property/DslProperties.css
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/property/DslProperties.css
@@ -248,10 +248,15 @@
min-height: 6px;
}
+.karavan .properties .header-menu-toggle {
+ padding-left: 6px;
+ padding-right: 0;
+}
+
.karavan .properties .component-headers {
margin-left: 24px;
}
.karavan .properties .component-headers .pf-v5-c-clipboard-copy.pf-m-inline {
background-color: transparent;
-}
\ No newline at end of file
+}
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/property/DslProperties.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/property/DslProperties.tsx
index 6169d656..ed56d2eb 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/property/DslProperties.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/property/DslProperties.tsx
@@ -27,7 +27,7 @@ import {
MenuToggleElement,
MenuToggle,
DropdownList,
- DropdownItem, Label, Flex, LabelGroup, Popover, FlexItem, Badge, ClipboardCopy, ClipboardCopyAction,
+ DropdownItem, Flex, Popover, FlexItem, Badge, ClipboardCopy,
} from '@patternfly/react-core';
import '../karavan.css';
import './DslProperties.css';
@@ -85,18 +85,16 @@ export function DslProperties(props: Props) {
const showMenu = hasSteps || targetDsl !== undefined;
return showMenu ?
<Dropdown
- style={{inset: "0px auto auto -70px important!"}}
- className={"xxx"}
+ popperProps={{position: "end"}}
isOpen={isMenuOpen}
onSelect={() => {
}}
onOpenChange={(isOpen: boolean) => setMenuOpen(isOpen)}
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
<MenuToggle
- style={{width: "240px", display: "flex", flexDirection: "row", justifyContent: "end"}}
- className={"zzzz"}
+ className="header-menu-toggle"
ref={toggleRef}
- aria-label="kebab dropdown toggle"
+ aria-label="menu"
variant="plain"
onClick={() => setMenuOpen(!isMenuOpen)}
isExpanded={isMenuOpen}
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ComponentPropertyField.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ComponentPropertyField.tsx
index ae63e323..a25acafc 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ComponentPropertyField.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ComponentPropertyField.tsx
@@ -21,10 +21,13 @@ import {
Popover,
Switch,
InputGroup,
- TextArea,
Tooltip,
Button,
- capitalize, InputGroupItem, TextInputGroup, TextVariants, Text
+ capitalize,
+ InputGroupItem,
+ TextInputGroup,
+ TextVariants,
+ Text,
} from '@patternfly/react-core';
import {
Select,
@@ -51,6 +54,7 @@ import {shallow} from "zustand/shallow";
import {KubernetesIcon} from "../../icons/ComponentIcons";
import EditorIcon from "@patternfly/react-icons/dist/js/icons/code-icon";
import {ModalEditor} from "./ModalEditor";
+import {ComponentPropertyPlaceholderDropdown} from "./ComponentPropertyPlaceholderDropdown";
const prefix = "parameters";
const beanPrefix = "#bean:";
@@ -67,16 +71,18 @@ export function ComponentPropertyField(props: Props) {
const {onParametersChange, getInternalComponentName} = usePropertiesHook();
const [integration] = useIntegrationStore((state) => [state.integration], shallow)
- const [dark, setSelectedStep] = useDesignerStore((s) => [s.dark, s.setSelectedStep], shallow)
+ const [dark, setSelectedStep, propertyPlaceholders] = useDesignerStore((s) =>
+ [s.dark, s.setSelectedStep, s.propertyPlaceholders], shallow)
const [selectStatus, setSelectStatus] = useState<Map<string, boolean>>(new Map<string, boolean>());
const [showEditor, setShowEditor] = useState<boolean>(false);
const [showPassword, setShowPassword] = useState<boolean>(false);
const [infrastructureSelector, setInfrastructureSelector] = useState<boolean>(false);
const [infrastructureSelectorProperty, setInfrastructureSelectorProperty] = useState<string | undefined>(undefined);
-
const [id, setId] = useState<string>(prefix + "-" + props.property.name);
+
const ref = useRef<any>(null);
+ const [isOpenPlaceholdersDropdown, setOpenPlaceholdersDropdown] = useState<boolean>(false);
function parametersChanged(parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) {
@@ -262,20 +268,30 @@ export function ComponentPropertyField(props: Props) {
</Button>
</Tooltip>
}
+ <InputGroupItem>
+ <ComponentPropertyPlaceholderDropdown property={property}/>
+ </InputGroupItem>
</InputGroup>
}
- function getTextInput(property: ComponentProperty, value: any) {
+ function getSpecialStringInput(property: ComponentProperty, value: any) {
return (
- <TextInput
- className="text-field" isRequired
- type={(property.secret ? "password" : "text")}
- id={id} name={id}
- value={value !== undefined ? value : property.defaultValue}
- customIcon={<Text component={TextVariants.p}>{property.type}</Text>}
- onChange={(_, value) => {
- parametersChanged(property.name, value, property.kind === 'path')
- }}/>
+ <InputGroup>
+ <InputGroupItem isFill>
+ <TextInput
+ className="text-field" isRequired
+ type={(property.secret ? "password" : "text")}
+ id={id} name={id}
+ value={value !== undefined ? value : property.defaultValue}
+ customIcon={<Text component={TextVariants.p}>{property.type}</Text>}
+ onChange={(_, value) => {
+ parametersChanged(property.name, value, property.kind === 'path')
+ }}/>
+ </InputGroupItem>
+ <InputGroupItem>
+ <ComponentPropertyPlaceholderDropdown property={property}/>
+ </InputGroupItem>
+ </InputGroup>
)
}
@@ -330,6 +346,9 @@ export function ComponentPropertyField(props: Props) {
onChange={(_, v) => parametersChanged(property.name, v)}
/>
</InputGroupItem>
+ <InputGroupItem>
+ <ComponentPropertyPlaceholderDropdown property={property}/>
+ </InputGroupItem>
</TextInputGroup>
)
}
@@ -362,7 +381,7 @@ export function ComponentPropertyField(props: Props) {
{property.type === 'string' && property.enum === undefined && !canBeInternalUri(property)
&& getStringInput(property, value)}
{['duration', 'integer', 'int', 'number'].includes(property.type) && property.enum === undefined && !canBeInternalUri(property)
- && getTextInput(property, value)}
+ && getSpecialStringInput(property, value)}
{['object'].includes(property.type) && !property.enum
&& getSelectBean(property, value)}
{['string', 'object'].includes(property.type) && property.enum
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ComponentPropertyPlaceholderDropdown.css b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ComponentPropertyPlaceholderDropdown.css
new file mode 100644
index 00000000..7d5d2d43
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ComponentPropertyPlaceholderDropdown.css
@@ -0,0 +1,24 @@
+/*
+ * 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 .properties .property-placeholder-toggle {
+ padding-left: 6px;
+ padding-right: 6px;
+}
+.karavan .properties .property-placeholder-toggle .pf-v5-c-menu-toggle__controls {
+ display: none;
+}
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ComponentPropertyPlaceholderDropdown.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ComponentPropertyPlaceholderDropdown.tsx
new file mode 100644
index 00000000..270f9549
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ComponentPropertyPlaceholderDropdown.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, {useState} from 'react';
+import {
+ Dropdown,
+ MenuToggleElement,
+ MenuToggle,
+ DropdownList, DropdownItem
+} from '@patternfly/react-core';
+import '../../karavan.css';
+import './ComponentPropertyPlaceholderDropdown.css';
+import "@patternfly/patternfly/patternfly.css";
+import {ComponentProperty} from "karavan-core/lib/model/ComponentModels";
+import {RouteToCreate} from "../../utils/CamelUi";
+import {usePropertiesHook} from "../usePropertiesHook";
+import {useDesignerStore} from "../../DesignerStore";
+import {shallow} from "zustand/shallow";
+import EllipsisVIcon from "@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon";
+
+
+interface Props {
+ property: ComponentProperty,
+}
+
+export function ComponentPropertyPlaceholderDropdown(props: Props) {
+
+ const {onParametersChange} = usePropertiesHook();
+ const [propertyPlaceholders] = useDesignerStore((s) => [s.propertyPlaceholders], shallow)
+ const [isOpenPlaceholdersDropdown, setOpenPlaceholdersDropdown] = useState<boolean>(false);
+
+ function parametersChanged(parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) {
+ onParametersChange(parameter, value, pathParameter, newRoute);
+ }
+
+ const property: ComponentProperty = props.property;
+ return (
+ propertyPlaceholders && propertyPlaceholders.length > 0 ?
+ <Dropdown
+ popperProps={{position: "end"}}
+ isOpen={isOpenPlaceholdersDropdown}
+ onSelect={(_, value) => {
+ parametersChanged(property.name, `{{${value}}}`, property.kind === 'path')
+ setOpenPlaceholdersDropdown(false);
+ }}
+ onOpenChange={(isOpen: boolean) => setOpenPlaceholdersDropdown(isOpen)}
+ toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
+ <MenuToggle className="property-placeholder-toggle"
+ ref={toggleRef}
+ aria-label="placeholder menu"
+ variant="default"
+ onClick={() => setOpenPlaceholdersDropdown(!isOpenPlaceholdersDropdown)}
+ isExpanded={isOpenPlaceholdersDropdown}
+ >
+ <EllipsisVIcon/>
+ </MenuToggle>
+ )}
+ shouldFocusToggleOnSelect
+ >
+ <DropdownList>
+ {propertyPlaceholders.map((pp, index) =>
+ <DropdownItem value={pp} key={index}>{pp}</DropdownItem>
+ )}
+ </DropdownList>
+ </Dropdown>
+ : <></>
+ )
+}
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx
index aa093618..59066667 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx
@@ -74,6 +74,7 @@ import {
import {TemplateApi} from "karavan-core/lib/api/TemplateApi";
import {KubernetesIcon} from "../../icons/ComponentIcons";
import {BeanProperties} from "./BeanProperties";
+import {ComponentPropertyPlaceholderDropdown} from "./ComponentPropertyPlaceholderDropdown";
interface Props {
property: PropertyMeta,
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/FileEditor.tsx b/karavan-web/karavan-app/src/main/webui/src/project/FileEditor.tsx
index 7193243a..6fe00b39 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/FileEditor.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/FileEditor.tsx
@@ -14,15 +14,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import React from 'react';
+import React, {useEffect, useState} from 'react';
import '../designer/karavan.css';
import Editor from "@monaco-editor/react";
import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
-import {ProjectFile} from "../api/ProjectModels";
+import {Project, ProjectFile} from "../api/ProjectModels";
import {useFilesStore, useFileStore} from "../api/ProjectStore";
import {KaravanDesigner} from "../designer/KaravanDesigner";
import {ProjectService} from "../api/ProjectService";
import {shallow} from "zustand/shallow";
+import {KaravanApi} from "../api/KaravanApi";
+import {getPropertyPlaceholders} from "../util/StringUtils";
interface Props {
projectId: string
@@ -37,6 +39,17 @@ const languages = new Map<string, string>([
export function FileEditor (props: Props) {
const [file, designerTab] = useFileStore((s) => [s.file, s.designerTab], shallow )
+ const [files] = useFilesStore((s) => [s.files], shallow);
+ const [propertyPlaceholders, setPropertyPlaceholders] = useState<string[]>([]);
+
+ useEffect(() => {
+ const pp = getPropertyPlaceholders(files);
+ setPropertyPlaceholders(prevState => {
+ prevState.length = 0;
+ prevState.push(...pp);
+ return prevState;
+ })
+ }, []);
function save (name: string, code: string) {
if (file) {
@@ -63,6 +76,7 @@ export function FileEditor (props: Props) {
onSaveCustomCode={(name, code) =>
ProjectService.updateFile(new ProjectFile(name + ".java", props.projectId, code, Date.now()), false)}
onGetCustomCode={onGetCustomCode}
+ propertyPlaceholders={propertyPlaceholders}
/>
)
}
diff --git a/karavan-web/karavan-app/src/main/webui/src/util/StringUtils.ts b/karavan-web/karavan-app/src/main/webui/src/util/StringUtils.ts
index 407778f1..6b8a8986 100644
--- a/karavan-web/karavan-app/src/main/webui/src/util/StringUtils.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/util/StringUtils.ts
@@ -1,3 +1,24 @@
+import {ProjectFile} from "../api/ProjectModels";
+
export function isEmpty(str: string) {
return !str?.trim();
+}
+
+export function getPropertyPlaceholders(files: ProjectFile[]): string[] {
+ const result: string[] = []
+ const file = files.filter(f => f.name === 'application.properties')?.at(0);
+ if (file) {
+ const code = file.code;
+ const lines = code.split('\n').map((line) => line.trim());
+ lines
+ .filter(line => !line.startsWith("camel.") && !line.startsWith("jkube.") && !line.startsWith("jib."))
+ .filter(line => line !== undefined && line !== null && line.length > 0)
+ .forEach(line => {
+ const parts = line.split("=");
+ if (parts.length > 0) {
+ result.push(parts[0]);
+ }
+ })
+ }
+ return result;
}
\ No newline at end of file