Skip to content

Commit

Permalink
Dsl properties apache#1098
Browse files Browse the repository at this point in the history
  • Loading branch information
mgubaidullin committed Feb 3, 2024
1 parent cb23be3 commit 4da2d8b
Show file tree
Hide file tree
Showing 11 changed files with 477 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ export function ComponentPropertyField(props: Props) {
</Tooltip>
}
<InputGroupItem>
<PropertyPlaceholderDropdown property={property} value={value}/>
<PropertyPlaceholderDropdown property={property} value={value} onComponentPropertyChange={onParametersChange}/>
</InputGroupItem>
</InputGroup>
}
Expand All @@ -287,7 +287,7 @@ export function ComponentPropertyField(props: Props) {
}}/>
</InputGroupItem>
<InputGroupItem>
<PropertyPlaceholderDropdown property={property} value={value}/>
<PropertyPlaceholderDropdown property={property} value={value} onComponentPropertyChange={onParametersChange}/>
</InputGroupItem>
</InputGroup>
)
Expand Down Expand Up @@ -345,7 +345,7 @@ export function ComponentPropertyField(props: Props) {
/>
</InputGroupItem>
<InputGroupItem>
<PropertyPlaceholderDropdown property={property} value={value}/>
<PropertyPlaceholderDropdown property={property} value={value} onComponentPropertyChange={onParametersChange}/>
</InputGroupItem>
</TextInputGroup>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,9 @@ export function DslPropertyField(props: Props) {
onChange={(_, v) => propertyChanged(property.name, v)}
/>
</InputGroupItem>
<InputGroupItem>
<PropertyPlaceholderDropdown property={property} value={value} onDslPropertyChange={propertyChanged}/>
</InputGroupItem>
</TextInputGroup>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@ import '../../karavan.css';
import './PropertyPlaceholderDropdown.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";
import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-icon";
import {InfrastructureAPI} from "../../utils/InfrastructureAPI";
import {PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
import {RouteToCreate} from "../../utils/CamelUi";

const SYNTAX_EXAMPLES = [
{key: 'property:', value: 'group.property', description: 'Application property'},
Expand All @@ -50,13 +51,14 @@ const SYNTAX_EXAMPLES = [
]

interface Props {
property: ComponentProperty,
value: any
property: ComponentProperty | PropertyMeta,
value: any,
onDslPropertyChange?: (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => void,
onComponentPropertyChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => void
}

export function PropertyPlaceholderDropdown(props: Props) {

const {onParametersChange} = usePropertiesHook();
const [propertyPlaceholders, setPropertyPlaceholders] = useDesignerStore((s) =>
[s.propertyPlaceholders, s.setPropertyPlaceholders], shallow)
const [isOpenPlaceholdersDropdown, setOpenPlaceholdersDropdown] = useState<boolean>(false);
Expand All @@ -78,8 +80,12 @@ export function PropertyPlaceholderDropdown(props: Props) {

const hasPlaceholders = (propertyPlaceholders && propertyPlaceholders.length > 0 );

function parametersChanged(parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) {
onParametersChange(parameter, value, pathParameter, newRoute);
function parametersChanged(value: string | number | boolean | any) {
if (property instanceof ComponentProperty) {
props.onComponentPropertyChange?.(property.name, `{{${value}}}`, property.kind === 'path');
} else {
props.onDslPropertyChange?.(property.name, `{{${value}}}`);
}
}

function onMenuToggleClick() {
Expand Down Expand Up @@ -151,7 +157,7 @@ export function PropertyPlaceholderDropdown(props: Props) {
popperProps={{position: "end"}}
isOpen={isOpenPlaceholdersDropdown}
onSelect={(_, value) => {
parametersChanged(property.name, `{{${value}}}`, property.kind === 'path')
parametersChanged(value);
setOpenPlaceholdersDropdown(false);
}}
onOpenChange={(isOpen: boolean) => setOpenPlaceholdersDropdown(isOpen)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +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";
import {PropertyPlaceholderDropdown} from "./PropertyPlaceholderDropdown";

const prefix = "parameters";
const beanPrefix = "#bean:";
Expand Down Expand Up @@ -267,7 +267,7 @@ export function ComponentPropertyField(props: Props) {
</Tooltip>
}
<InputGroupItem>
<ComponentPropertyPlaceholderDropdown property={property} value={value}/>
<PropertyPlaceholderDropdown property={property} value={value} onComponentPropertyChange={onParametersChange}/>
</InputGroupItem>
</InputGroup>
}
Expand All @@ -287,7 +287,7 @@ export function ComponentPropertyField(props: Props) {
}}/>
</InputGroupItem>
<InputGroupItem>
<ComponentPropertyPlaceholderDropdown property={property} value={value}/>
<PropertyPlaceholderDropdown property={property} value={value} onComponentPropertyChange={onParametersChange}/>
</InputGroupItem>
</InputGroup>
)
Expand Down Expand Up @@ -345,7 +345,7 @@ export function ComponentPropertyField(props: Props) {
/>
</InputGroupItem>
<InputGroupItem>
<ComponentPropertyPlaceholderDropdown property={property} value={value}/>
<PropertyPlaceholderDropdown property={property} value={value} onComponentPropertyChange={onParametersChange}/>
</InputGroupItem>
</TextInputGroup>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ import {
import {TemplateApi} from "karavan-core/lib/api/TemplateApi";
import {KubernetesIcon} from "../../icons/ComponentIcons";
import {BeanProperties} from "./BeanProperties";
import {ComponentPropertyPlaceholderDropdown} from "./ComponentPropertyPlaceholderDropdown";
import {PropertyPlaceholderDropdown} from "./PropertyPlaceholderDropdown";

interface Props {
property: PropertyMeta,
Expand Down Expand Up @@ -423,6 +423,9 @@ export function DslPropertyField(props: Props) {
onChange={(_, v) => propertyChanged(property.name, v)}
/>
</InputGroupItem>
<InputGroupItem>
<PropertyPlaceholderDropdown property={property} value={value} onDslPropertyChange={propertyChanged}/>
</InputGroupItem>
</TextInputGroup>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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-button__icon.pf-m-start {
margin-inline-end: 0;
}

.karavan .properties .property-placeholder-toggle .pf-v5-c-menu-toggle__controls {
display: none;
}

.pf-v5-c-popover .property-placeholder-toggle-form {
width: 300px;
}

.pf-v5-c-popover .property-placeholder-toggle-form .pf-v5-c-form__group {
grid-template-columns: 1fr 2fr;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*
* 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,
Popover,
Flex,
TextInput,
FormGroup,
Form,
Button,
FlexItem,
DropdownGroup, Divider
} from '@patternfly/react-core';
import '../../karavan.css';
import './PropertyPlaceholderDropdown.css';
import "@patternfly/patternfly/patternfly.css";
import {ComponentProperty} from "karavan-core/lib/model/ComponentModels";
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";
import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-icon";
import {InfrastructureAPI} from "../../utils/InfrastructureAPI";
import {PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
import {RouteToCreate} from "../../utils/CamelUi";

const SYNTAX_EXAMPLES = [
{key: 'property:', value: 'group.property', description: 'Application property'},
{key: 'env:', value: 'env:ENV_NAME', description: 'OS environment variable'},
{key: 'sys:', value: 'sys:JvmPropertyName', description: 'JVM system property'},
{key: 'bean:', value: 'bean:beanName.method', description: 'Bean’s method'}
]

interface Props {
property: ComponentProperty | PropertyMeta,
value: any,
onDslPropertyChange?: (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => void,
onComponentPropertyChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => void
}

export function PropertyPlaceholderDropdown(props: Props) {

const [propertyPlaceholders, setPropertyPlaceholders] = useDesignerStore((s) =>
[s.propertyPlaceholders, s.setPropertyPlaceholders], shallow)
const [isOpenPlaceholdersDropdown, setOpenPlaceholdersDropdown] = useState<boolean>(false);
const [propValue, setPropValue] = useState<string>('');
const [isVisible, setIsVisible] = React.useState(false);

function removeBrackets(val: string) {
return val.replace('{{', '').replace('}}', '');
}

const {property, value} = props;
const valueIsPlaceholder: boolean = value && value.toString().startsWith('{{') && value.toString().endsWith('}}');
const placeholderValue = valueIsPlaceholder ? value.toString().replace('{{', '').replace('}}', '') : undefined;
const showAddButton = valueIsPlaceholder
&& !propertyPlaceholders.includes(placeholderValue)
&& !SYNTAX_EXAMPLES.map(se=> se.value).includes(removeBrackets(placeholderValue))
&& SYNTAX_EXAMPLES.findIndex(se=> removeBrackets(placeholderValue).startsWith(se.key)) === -1;
const popoverId = "popover-selector-" + property.name;

const hasPlaceholders = (propertyPlaceholders && propertyPlaceholders.length > 0 );

function parametersChanged(value: string | number | boolean | any) {
if (property instanceof ComponentProperty) {
props.onComponentPropertyChange?.(property.name, `{{${value}}}`, property.kind === 'path');
} else {
props.onDslPropertyChange?.(property.name, `{{${value}}}`);
}
}

function onMenuToggleClick() {
if (!showAddButton) {
setOpenPlaceholdersDropdown(!isOpenPlaceholdersDropdown)
}
}

function saveProperty() {
InfrastructureAPI.onSavePropertyPlaceholder(placeholderValue, propValue);
setIsVisible(false);
const p = [...propertyPlaceholders]
p.push(placeholderValue);
setPropertyPlaceholders(p);
}

function getPopover() {
return (
<Popover
isVisible={isVisible}
shouldOpen={(_event, _fn) => setIsVisible(true)}
shouldClose={(_event, _fn) => setIsVisible(false)}
aria-label="Add property"
headerContent={"Add property"}
bodyContent={
<Form isHorizontal className="property-placeholder-toggle-form" autoComplete="off">
<FormGroup isInline label="Property" isRequired fieldId="property">
<TextInput id="property" readOnly value={placeholderValue}/>
</FormGroup>
<FormGroup isInline label="Value" isRequired fieldId="value">
<TextInput id="value" isRequired value={propValue}
onChange={(_, value) => setPropValue(value)}/>
</FormGroup>
</Form>
}
footerContent={
<Flex>
<FlexItem align={{default: "alignRight"}}>
<Button
onClick={() => saveProperty()}>
Save
</Button>
</FlexItem>
</Flex>
}
triggerRef={() => document.getElementById(popoverId) as HTMLButtonElement}
/>
)
}

function getToggle(toggleRef: React.Ref<MenuToggleElement>) {
return (
<MenuToggle className="property-placeholder-toggle"
id={popoverId}
ref={toggleRef}
aria-label="placeholder menu"
variant="default"
onClick={() => onMenuToggleClick()}
isExpanded={isOpenPlaceholdersDropdown}
>
{showAddButton ? <AddIcon/> : <EllipsisVIcon/>}
{showAddButton && getPopover()}
</MenuToggle>
)
}

return (
<Dropdown
popperProps={{position: "end"}}
isOpen={isOpenPlaceholdersDropdown}
onSelect={(_, value) => {
parametersChanged(value);
setOpenPlaceholdersDropdown(false);
}}
onOpenChange={(isOpen: boolean) => setOpenPlaceholdersDropdown(isOpen)}
toggle={(toggleRef: React.Ref<MenuToggleElement>) => getToggle(toggleRef)}
shouldFocusToggleOnSelect
>
<DropdownList>
{hasPlaceholders && <DropdownGroup label="Application Properties">
{propertyPlaceholders.map((pp, index) =>
<DropdownItem value={pp} key={index}>{pp}</DropdownItem>
)}
</DropdownGroup>}
{hasPlaceholders && <Divider component="li"/>}
<DropdownGroup label="Syntax examples">
{SYNTAX_EXAMPLES.map(se =>
<DropdownItem value={se.value} key={se.key} description={se.description}>
{se.value}
</DropdownItem>)
}
</DropdownGroup>
</DropdownList>
</Dropdown>
)
}
Loading

0 comments on commit 4da2d8b

Please sign in to comment.