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 ceeffb4 Saas feature28 (#412) ceeffb4 is described below commit ceeffb46ffcdc270ec7ac1b07df192918bd2d374 Author: Marat Gubaidullin <marat.gubaidul...@gmail.com> AuthorDate: Tue Jul 12 17:12:58 2022 -0400 Saas feature28 (#412) * Set/Remove property * UI fixes * Select values from COnfigMap, secrets and services * Kubernetes resources in Kamelets and Components --- .../camel/karavan/api/KubernetesResource.java | 33 ++++ .../camel/karavan/service/KubernetesService.java | 42 +++-- karavan-app/src/main/webapp/src/api/KaravanApi.tsx | 37 ++++ .../main/webapp/src/projects/ProjectDashboard.tsx | 21 +-- .../src/main/webapp/src/projects/ProjectHeader.tsx | 3 +- .../src/main/webapp/src/projects/ProjectPage.tsx | 29 +++- karavan-core/src/core/api/ComponentApi.ts | 2 - karavan-designer/src/App.tsx | 3 +- karavan-designer/src/designer/KaravanDesigner.tsx | 8 +- .../designer/route/property/DslPropertyField.tsx | 163 ++++++++++++----- .../route/property/KameletPropertyField.tsx | 141 ++++++++++----- .../designer/route/property/KubernetesSelector.tsx | 193 +++++++++++++++++++++ karavan-designer/src/designer/utils/CamelUi.tsx | 11 +- .../src/designer/utils/KubernetesAPI.ts | 19 ++ 14 files changed, 576 insertions(+), 129 deletions(-) diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/api/KubernetesResource.java b/karavan-app/src/main/java/org/apache/camel/karavan/api/KubernetesResource.java index 22ee3a5..b97c1e6 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/api/KubernetesResource.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/api/KubernetesResource.java @@ -139,4 +139,37 @@ public class KubernetesResource { } return Response.noContent().build(); } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/configmap/{environment}") + public Response getConfigMaps(@HeaderParam("username") String username, @PathParam("environment") String environment) throws Exception { + Optional<KaravanConfiguration.Environment> env = configuration.environments().stream().filter(e -> e.name().equals(environment)).findFirst(); + if (env.isPresent()) { + return Response.ok(kubernetesService.getConfigMaps(env.get().namespace())).build(); + } + return Response.noContent().build(); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/secret/{environment}") + public Response getSecrets(@HeaderParam("username") String username, @PathParam("environment") String environment) throws Exception { + Optional<KaravanConfiguration.Environment> env = configuration.environments().stream().filter(e -> e.name().equals(environment)).findFirst(); + if (env.isPresent()) { + return Response.ok(kubernetesService.getSecrets(env.get().namespace())).build(); + } + return Response.noContent().build(); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/service/{environment}") + public Response getServices(@HeaderParam("username") String username, @PathParam("environment") String environment) throws Exception { + Optional<KaravanConfiguration.Environment> env = configuration.environments().stream().filter(e -> e.name().equals(environment)).findFirst(); + if (env.isPresent()) { + return Response.ok(kubernetesService.getServices(env.get().namespace())).build(); + } + return Response.noContent().build(); + } } \ No newline at end of file diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java index 23a868e..d72abe7 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java @@ -20,8 +20,6 @@ import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.client.Config; -import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.openshift.api.model.DeploymentConfig; @@ -34,9 +32,6 @@ import io.fabric8.tekton.pipeline.v1beta1.PipelineRun; import io.fabric8.tekton.pipeline.v1beta1.PipelineRunBuilder; import io.fabric8.tekton.pipeline.v1beta1.PipelineRunSpec; import io.fabric8.tekton.pipeline.v1beta1.PipelineRunSpecBuilder; -import io.quarkus.kubernetes.client.runtime.KubernetesClientBuildConfig; -import io.smallrye.mutiny.tuples.Tuple2; -import io.smallrye.mutiny.tuples.Tuple3; import org.apache.camel.karavan.model.DeploymentStatus; import org.apache.camel.karavan.model.PipelineRunLog; import org.apache.camel.karavan.model.PodStatus; @@ -47,8 +42,6 @@ import org.jboss.logging.Logger; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.Produces; import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -58,7 +51,7 @@ import java.util.stream.Collectors; public class KubernetesService { @ConfigProperty(name = "kubernetes.namespace", defaultValue = "localhost") - String namespace; + String currentNamespace; @Produces public KubernetesClient kubernetesClient() { @@ -220,11 +213,40 @@ public class KubernetesService { } } + public List<String> getConfigMaps(String namespace) { + List<String> result = new ArrayList<>(); + kubernetesClient().configMaps().inNamespace(namespace).list().getItems().forEach(configMap -> { + System.out.println(configMap.getMetadata().getName()); + String name = configMap.getMetadata().getName(); + configMap.getData().keySet().forEach(data -> result.add(name + "/" + data)); + }); + return result; + } + + public List<String> getSecrets(String namespace) { + List<String> result = new ArrayList<>(); + kubernetesClient().secrets().inNamespace(namespace).list().getItems().forEach(secret -> { + String name = secret.getMetadata().getName(); + secret.getData().keySet().forEach(data -> result.add(name + "/" + data)); + }); + return result; + } + + public List<String> getServices(String namespace) { + List<String> result = new ArrayList<>(); + kubernetesClient().services().inNamespace(namespace).list().getItems().forEach(service -> { + String name = service.getMetadata().getName(); + String host = name + "." + namespace + ".svc.cluster.local"; + service.getSpec().getPorts().forEach(port -> result.add(name + "|" + host + ":" + port.getPort())); + }); + return result; + } + public Secret getKaravanSecret() { - return kubernetesClient().secrets().inNamespace(namespace).withName("karavan").get(); + return kubernetesClient().secrets().inNamespace(currentNamespace).withName("karavan").get(); } public boolean inKubernetes() { - return !Objects.equals(namespace, "localhost"); + return !Objects.equals(currentNamespace, "localhost"); } } diff --git a/karavan-app/src/main/webapp/src/api/KaravanApi.tsx b/karavan-app/src/main/webapp/src/api/KaravanApi.tsx index b1868e7..239d688 100644 --- a/karavan-app/src/main/webapp/src/api/KaravanApi.tsx +++ b/karavan-app/src/main/webapp/src/api/KaravanApi.tsx @@ -187,6 +187,43 @@ export const KaravanApi = { }); }, + getConfigMaps: async (environment: string, after: (any: []) => void) => { + axios.get('/kubernetes/configmap/' + environment, + {headers: {'Accept': 'application/json', 'Content-Type': 'application/json', 'username': 'cameleer'}}) + .then(res => { + if (res.status === 200) { + after(res.data); + } + }).catch(err => { + console.log(err); + }); + }, + + getSecrets: async (environment: string, after: (any: []) => void) => { + axios.get('/kubernetes/secret/' + environment, + {headers: {'Accept': 'application/json', 'Content-Type': 'application/json', 'username': 'cameleer'}}) + .then(res => { + if (res.status === 200) { + after(res.data); + } + }).catch(err => { + console.log(err); + }); + }, + + getServices: async (environment: string, after: (any: []) => void) => { + axios.get('/kubernetes/service/' + environment, + {headers: {'Accept': 'application/json', 'Content-Type': 'application/json', 'username': 'cameleer'}}) + .then(res => { + if (res.status === 200) { + after(res.data); + } + }).catch(err => { + console.log(err); + }); + }, + + getKameletNames: async (after: (names: []) => void) => { axios.get('/kamelet', {headers: {'Accept': 'application/json'}}) diff --git a/karavan-app/src/main/webapp/src/projects/ProjectDashboard.tsx b/karavan-app/src/main/webapp/src/projects/ProjectDashboard.tsx index 6f92e77..40e7b6c 100644 --- a/karavan-app/src/main/webapp/src/projects/ProjectDashboard.tsx +++ b/karavan-app/src/main/webapp/src/projects/ProjectDashboard.tsx @@ -1,32 +1,25 @@ import React from 'react'; import { Badge, - Button, Text, - DescriptionList, - DescriptionListTerm, - DescriptionListGroup, - DescriptionListDescription, Card, - CardBody, Spinner, Tooltip, Flex, FlexItem, Tabs, Tab, PageSection + CardBody, Flex, FlexItem, } from '@patternfly/react-core'; import '../designer/karavan.css'; import {KaravanApi} from "../api/KaravanApi"; import {Project, ProjectFileTypes, ProjectStatus} from "../models/ProjectModels"; -import BuildIcon from "@patternfly/react-icons/dist/esm/icons/build-icon"; -import PushIcon from "@patternfly/react-icons/dist/esm/icons/code-branch-icon"; -import {ChartDonut, ChartDonutThreshold, ChartDonutUtilization} from "@patternfly/react-charts"; +import {ChartDonut} from "@patternfly/react-charts"; interface Props { project: Project, config: any, + environments: string [], } interface State { project?: Project, status?: ProjectStatus, - environments: string[], - environment: string, + key?: string, } @@ -34,10 +27,6 @@ export class ProjectDashboard extends React.Component<Props, State> { public state: State = { project: this.props.project, - environments: this.props.config.environments && Array.isArray(this.props.config.environments) - ? Array.from(this.props.config.environments) : [], - environment: this.props.config.environments && Array.isArray(this.props.config.environments) - ? this.props.config.environments[0] : '' }; interval: any; @@ -143,7 +132,7 @@ export class ProjectDashboard extends React.Component<Props, State> { } render() { - const {project, environments, status} = this.state; + const { environments } = this.props; return ( <Flex direction={{default: "column"}}> {environments.filter(e => e !== undefined) diff --git a/karavan-app/src/main/webapp/src/projects/ProjectHeader.tsx b/karavan-app/src/main/webapp/src/projects/ProjectHeader.tsx index 2391c0e..29880f9 100644 --- a/karavan-app/src/main/webapp/src/projects/ProjectHeader.tsx +++ b/karavan-app/src/main/webapp/src/projects/ProjectHeader.tsx @@ -14,6 +14,7 @@ import {ProjectInfo} from "./ProjectInfo"; interface Props { project: Project, config: any, + environments: string [], showLog: (type: 'container' | 'pipeline', name: string, environment: string) => void deleteEntity: (type: 'pod' | 'deployment', name: string, environment: string) => void } @@ -44,7 +45,7 @@ export class ProjectHeader extends React.Component<Props, State> { <FlexItem> <PageSection padding={{default: "padding"}}> {tab === 'details' && <ProjectInfo project={this.props.project} config={this.props.config} deleteEntity={this.props.deleteEntity} showLog={this.props.showLog}/>} - {tab === 'dashboard' && <ProjectDashboard project={this.props.project} config={this.props.config}/>} + {tab === 'dashboard' && <ProjectDashboard environments={this.props.environments} project={this.props.project} config={this.props.config}/>} </PageSection> </FlexItem> </Flex> diff --git a/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx b/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx index faf732c..a8dcf06 100644 --- a/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx +++ b/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx @@ -42,6 +42,7 @@ import {PropertiesEditor} from "./PropertiesEditor"; import {ProjectHeader} from "./ProjectHeader"; import {ProjectModel, ProjectProperty} from "karavan-core/lib/model/ProjectModel"; import {ProjectModelApi} from "karavan-core/lib/api/ProjectModelApi"; +import {KubernetesAPI} from "../designer/utils/KubernetesAPI"; interface Props { project: Project, @@ -60,6 +61,8 @@ interface State { mode: "design" | "code", editAdvancedProperties: boolean key: string + environments: string[], + environment: string, } export class ProjectPage extends React.Component<Props, State> { @@ -72,7 +75,11 @@ export class ProjectPage extends React.Component<Props, State> { files: [], mode: "design", editAdvancedProperties: false, - key: '' + key: '', + environments: this.props.config.environments && Array.isArray(this.props.config.environments) + ? Array.from(this.props.config.environments) : [], + environment: this.props.config.environments && Array.isArray(this.props.config.environments) + ? this.props.config.environments[0] : '' }; componentDidMount() { @@ -91,15 +98,25 @@ export class ProjectPage extends React.Component<Props, State> { files: files }) }); + KubernetesAPI.inKubernetes = true; + KaravanApi.getConfigMaps(this.state.environment, (any: []) => { + KubernetesAPI.setConfigMaps(any); + }); + KaravanApi.getSecrets(this.state.environment, (any: []) => { + KubernetesAPI.setSecrets(any); + }); + KaravanApi.getServices(this.state.environment, (any: []) => { + KubernetesAPI.setServices(any); + }); } } post = (file: ProjectFile) => { KaravanApi.postProjectFile(file, res => { if (res.status === 200) { - console.log(res) //TODO show notification + // console.log(res) //TODO show notification } else { - console.log(res) //TODO show notification + // console.log(res) //TODO show notification } }) } @@ -418,7 +435,11 @@ export class ProjectPage extends React.Component<Props, State> { {file === undefined && <PageSection isFilled className="kamelets-page project-page-section" padding={{default: file !== undefined ? 'noPadding' : 'noPadding'}}> - {<ProjectHeader project={this.props.project} config={this.props.config} showLog={this.showPipelineLog} deleteEntity={this.deleteEntity}/>} + {<ProjectHeader project={this.props.project} + config={this.props.config} + environments={this.state.environments} + showLog={this.showPipelineLog} + deleteEntity={this.deleteEntity}/>} {this.getProjectFiles()} </PageSection>} {showDesigner && this.getDesigner()} diff --git a/karavan-core/src/core/api/ComponentApi.ts b/karavan-core/src/core/api/ComponentApi.ts index 7d199f3..e656978 100644 --- a/karavan-core/src/core/api/ComponentApi.ts +++ b/karavan-core/src/core/api/ComponentApi.ts @@ -15,8 +15,6 @@ * limitations under the License. */ import {Component, ComponentProperty} from "../model/ComponentModels"; -import {CamelMetadataApi} from "../model/CamelMetadata"; -import {Kamelets} from "./KameletApi"; export const Components: Component[] = []; diff --git a/karavan-designer/src/App.tsx b/karavan-designer/src/App.tsx index 2b25490..2b55658 100644 --- a/karavan-designer/src/App.tsx +++ b/karavan-designer/src/App.tsx @@ -26,6 +26,7 @@ import {ComponentsPage} from "./components/ComponentsPage"; import {EipPage} from "./eip/EipPage"; import {BuilderPage} from "./builder/BuilderPage"; import {ProjectModel, StepStatus} from "karavan-core/lib/model/ProjectModel"; +import {KubernetesAPI} from "./designer/utils/KubernetesAPI"; interface Props { page: "designer" | "kamelets" | "components" | "eip" | "builder"; @@ -67,7 +68,6 @@ class App extends React.Component<Props, State> { }; componentDidMount() { - ["http-secured-sink.kamelet.yaml", "timer-source.kamelet.yaml", "http-secured-source.kamelet.yaml", @@ -107,7 +107,6 @@ class App extends React.Component<Props, State> { fetch("components/" + name) .then((r) => r.text()) .then(value => ComponentApi.saveComponent(value))); - } save(filename: string, yaml: string, propertyOnly: boolean) { diff --git a/karavan-designer/src/designer/KaravanDesigner.tsx b/karavan-designer/src/designer/KaravanDesigner.tsx index 6eb2ff8..20e9f60 100644 --- a/karavan-designer/src/designer/KaravanDesigner.tsx +++ b/karavan-designer/src/designer/KaravanDesigner.tsx @@ -139,7 +139,7 @@ export class KaravanDesigner extends React.Component<Props, State> { <Tab data-tour="routes" eventKey='routes' title={this.getTab("Routes", "Integration flows", "routes")}></Tab> <Tab eventKey='rest' title={this.getTab("REST", "REST services", "rest")}></Tab> <Tab eventKey='beans' title={this.getTab("Beans", "Beans Configuration", "beans")}></Tab> - <Tab eventKey='dependencies' title={this.getTab("Dependencies", "Dependencies", "dependencies")}></Tab> + {/*<Tab eventKey='dependencies' title={this.getTab("Dependencies", "Dependencies", "dependencies")}></Tab>*/} {/*<Tab eventKey='traits' title={this.getTab("Traits", "traits configuration", "traits")}></Tab>*/} <Tab eventKey='error' title={this.getTab("Error", "Error Handler", "error")}></Tab> <Tab eventKey='exception' title={this.getTab("Exceptions", "Exception Clauses per type", "exception")}></Tab> @@ -164,9 +164,9 @@ export class KaravanDesigner extends React.Component<Props, State> { {tab === 'exception' && <ExceptionDesigner integration={this.state.integration} onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)} dark={this.props.dark}/>} - {tab === 'traits' && <TraitsDesigner integration={this.state.integration} - onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)} - dark={this.props.dark}/>} + {/*{tab === 'traits' && <TraitsDesigner integration={this.state.integration}*/} + {/* onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}*/} + {/* dark={this.props.dark}/>}*/} {this.getHelpWindow()} </PageSection> ) diff --git a/karavan-designer/src/designer/route/property/DslPropertyField.tsx b/karavan-designer/src/designer/route/property/DslPropertyField.tsx index 6b03100..9141da0 100644 --- a/karavan-designer/src/designer/route/property/DslPropertyField.tsx +++ b/karavan-designer/src/designer/route/property/DslPropertyField.tsx @@ -23,7 +23,7 @@ import { Select, SelectVariant, SelectDirection, - SelectOption, ExpandableSection, TextArea, Chip, TextInputGroup, TextInputGroupMain, TextInputGroupUtilities, ChipGroup, Button, Text, Tooltip, Card + SelectOption, ExpandableSection, TextArea, Chip, TextInputGroup, TextInputGroupMain, TextInputGroupUtilities, ChipGroup, Button, Text, Tooltip, Card, InputGroup, Modal } from '@patternfly/react-core'; import '../../karavan.css'; import "@patternfly/patternfly/patternfly.css"; @@ -45,6 +45,11 @@ import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi"; import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon"; import {MediaTypes} from "../../utils/MediaTypes"; import {ComponentProperty} from "karavan-core/lib/model/ComponentModels"; +import CompressIcon from "@patternfly/react-icons/dist/js/icons/compress-icon"; +import ExpandIcon from "@patternfly/react-icons/dist/js/icons/expand-icon"; +import KubernetesIcon from "@patternfly/react-icons/dist/js/icons/openshift-icon"; +import {KubernetesSelector} from "./KubernetesSelector"; +import {KubernetesAPI} from "../../utils/KubernetesAPI"; interface Props { property: PropertyMeta, @@ -55,13 +60,16 @@ interface Props { onParameterChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => void, element?: CamelElement integration: Integration, - hideLabel?: boolean + hideLabel?: boolean, } interface State { selectStatus: Map<string, boolean>, - isShowAdvanced: Map<string, boolean> - arrayValues: Map<string, string> + isShowAdvanced: Map<string, boolean>, + arrayValues: Map<string, string>, + showEditor: boolean + showKubernetesSelector: boolean + kubernetesSelectorProperty?: string } export class DslPropertyField extends React.Component<Props, State> { @@ -70,7 +78,9 @@ export class DslPropertyField extends React.Component<Props, State> { selectStatus: new Map<string, boolean>(), arrayValues: new Map<string, string>(), isShowAdvanced: new Map<string, boolean>(), - } + showEditor: false, + showKubernetesSelector: false, + }; openSelect = (propertyName: string, isExpanded: boolean) => { this.setState({selectStatus: new Map<string, boolean>([[propertyName, isExpanded]])}); @@ -140,15 +150,72 @@ export class DslPropertyField extends React.Component<Props, State> { return property.name === 'uri' && !['ToDynamicDefinition', 'WireTapDefinition'].includes(dslName) } - getTextField = (property: PropertyMeta, value: any) => { + selectKubernetes = (value: string) => { + const propertyName = this.state.kubernetesSelectorProperty; + if (propertyName){ + if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}"; + this.propertyChanged(propertyName, value); + this.setState({showKubernetesSelector: false, kubernetesSelectorProperty: undefined}) + } + } + + openKubernetesSelector = (propertyName: string) => { + this.setState({kubernetesSelectorProperty: propertyName, showKubernetesSelector: true}); + } + + closeKubernetesSelector = () => { + this.setState({showKubernetesSelector: false}) + } + + getKubernetesSelectorModal() { return ( - <TextInput + <Modal + title="Select from Kubernetes" + width={'50%'} + className='dsl-modal' + isOpen={this.state.showKubernetesSelector} + onClose={() => this.closeKubernetesSelector()} + actions={{}}> + <KubernetesSelector + dark={false} + onSelect={this.selectKubernetes}/> + </Modal>) + } + + getStringInput = (property: PropertyMeta, value: any) => { + const showEditor = this.state.showEditor; + const inKubernetes = KubernetesAPI.inKubernetes; + const noKubeSelectorButton = ["uri", "id", "description", "group"].includes(property.name); + return (<InputGroup> + {inKubernetes && !showEditor && !noKubeSelectorButton && + <Tooltip position="bottom-end" content="Select from Kubernetes"> + <Button variant="control" onClick={e => this.openKubernetesSelector(property.name)}> + <KubernetesIcon/> + </Button> + </Tooltip>} + {(!showEditor || property.secret) && <TextInput className="text-field" isRequired isReadOnly={this.isUriReadOnly(property)} type={['integer', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")} id={property.name} name={property.name} value={value?.toString()} onChange={e => this.propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(e) : e)}/> - ) + } + {showEditor && !property.secret && <TextArea + autoResize={true} + className="text-field" isRequired isReadOnly={this.isUriReadOnly(property)} + type="text" + id={property.name} name={property.name} + value={value?.toString()} + onChange={e => this.propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(e) : e)}/> + } + {!property.secret && + <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}> + <Button variant="control" onClick={e => this.setState({showEditor: !showEditor})}> + {showEditor ? <CompressIcon/> : <ExpandIcon/>} + </Button> + </Tooltip> + } + </InputGroup>) } getTextArea = (property: PropertyMeta, value: any) => { @@ -462,10 +529,10 @@ export class DslPropertyField extends React.Component<Props, State> { headerContent={property.displayName} bodyContent={property.description} footerContent={ - <div> - {property.defaultValue !== undefined && property.defaultValue.toString().trim().length >0 && <div>{"Default: " + property.defaultValue}</div>} - {property.required && <b>Required</b>} - </div> + <div> + {property.defaultValue !== undefined && property.defaultValue.toString().trim().length >0 && <div>{"Default: " + property.defaultValue}</div>} + {property.required && <b>Required</b>} + </div> }> <button type="button" aria-label="More info" onClick={e => { e.preventDefault(); @@ -507,41 +574,43 @@ export class DslPropertyField extends React.Component<Props, State> { const property: PropertyMeta = this.props.property; const value = this.props.value; return ( - <FormGroup - data-tour={property.name} - label={this.props.hideLabel ? undefined : this.getLabel(property, value)} - isRequired={property.required} - fieldId={property.name} - labelIcon={this.getLabelIcon(property)}> - {value && ["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type) - && this.getExpressionField(property, value)} - {property.isObject && !property.isArray && !["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type) - && this.getObjectField(property, value)} - {property.isObject && property.isArray && !this.isMultiValueField(property) - && this.getMultiValueObjectField(property, value)} - {property.name === 'expression' && property.type === "string" && !property.isArray - && this.getTextArea(property, value)} - {this.canBeInternalUri(property, this.props.element) - && this.getInternalUriSelect(property, value)} - {this.canBeMediaType(property, this.props.element) - && this.getMediaTypeSelect(property, value)} - {['string', 'duration', 'integer', 'number'].includes(property.type) && property.name !== 'expression' && !property.name.endsWith("Ref") - && !property.isArray && !property.enumVals - && !this.canBeInternalUri(property, this.props.element) - && !this.canBeMediaType(property, this.props.element) - && this.getTextField(property, value)} - {['string'].includes(property.type) && property.name.endsWith("Ref") && !property.isArray && !property.enumVals - && this.getSelectBean(property, value)} - {this.isMultiValueField(property) - && this.getMultiValueField(property, value)} - {property.type === 'boolean' - && this.getSwitch(property, value)} - {property.enumVals - && this.getSelect(property, value)} - {isKamelet && property.name === 'parameters' && this.getKameletParameters()} - {!isKamelet && property.name === 'parameters' && this.getComponentParameters(property)} - - </FormGroup> + <div> + <FormGroup + data-tour={property.name} + label={this.props.hideLabel ? undefined : this.getLabel(property, value)} + isRequired={property.required} + fieldId={property.name} + labelIcon={this.getLabelIcon(property)}> + {value && ["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type) + && this.getExpressionField(property, value)} + {property.isObject && !property.isArray && !["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type) + && this.getObjectField(property, value)} + {property.isObject && property.isArray && !this.isMultiValueField(property) + && this.getMultiValueObjectField(property, value)} + {property.name === 'expression' && property.type === "string" && !property.isArray + && this.getTextArea(property, value)} + {this.canBeInternalUri(property, this.props.element) + && this.getInternalUriSelect(property, value)} + {this.canBeMediaType(property, this.props.element) + && this.getMediaTypeSelect(property, value)} + {['string', 'duration', 'integer', 'number'].includes(property.type) && property.name !== 'expression' && !property.name.endsWith("Ref") + && !property.isArray && !property.enumVals + && !this.canBeInternalUri(property, this.props.element) + && !this.canBeMediaType(property, this.props.element) + && this.getStringInput(property, value)} + {['string'].includes(property.type) && property.name.endsWith("Ref") && !property.isArray && !property.enumVals + && this.getSelectBean(property, value)} + {this.isMultiValueField(property) + && this.getMultiValueField(property, value)} + {property.type === 'boolean' + && this.getSwitch(property, value)} + {property.enumVals + && this.getSelect(property, value)} + {isKamelet && property.name === 'parameters' && this.getKameletParameters()} + {!isKamelet && property.name === 'parameters' && this.getComponentParameters(property)} + </FormGroup> + {this.getKubernetesSelectorModal()} + </div> ) } } diff --git a/karavan-designer/src/designer/route/property/KameletPropertyField.tsx b/karavan-designer/src/designer/route/property/KameletPropertyField.tsx index 39ff5d8..e48b23c 100644 --- a/karavan-designer/src/designer/route/property/KameletPropertyField.tsx +++ b/karavan-designer/src/designer/route/property/KameletPropertyField.tsx @@ -19,7 +19,7 @@ import { FormGroup, TextInput, Popover, - Switch, InputGroup, Button, TextArea, Text, Tooltip, + Switch, InputGroup, Button, TextArea, Text, Tooltip, Modal, } from '@patternfly/react-core'; import '../../karavan.css'; import "@patternfly/patternfly/patternfly.css"; @@ -27,6 +27,11 @@ import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon"; import ExpandIcon from "@patternfly/react-icons/dist/js/icons/expand-icon"; import CompressIcon from "@patternfly/react-icons/dist/js/icons/compress-icon"; import {Property} from "karavan-core/lib/model/KameletModels"; +import {KubernetesSelector} from "./KubernetesSelector"; +import {KubernetesAPI} from "../../utils/KubernetesAPI"; +import KubernetesIcon from "@patternfly/react-icons/dist/js/icons/openshift-icon"; +import ShowIcon from "@patternfly/react-icons/dist/js/icons/eye-icon"; +import HideIcon from "@patternfly/react-icons/dist/js/icons/eye-slash-icon"; interface Props { property: Property, @@ -37,6 +42,9 @@ interface Props { interface State { selectIsOpen: boolean showEditor: boolean + showPassword: boolean + showKubernetesSelector: boolean + kubernetesSelectorProperty?: string } export class KameletPropertyField extends React.Component<Props, State> { @@ -44,6 +52,8 @@ export class KameletPropertyField extends React.Component<Props, State> { public state: State = { selectIsOpen: false, showEditor: false, + showPassword: false, + showKubernetesSelector: false, } openSelect = () => { @@ -55,17 +65,58 @@ export class KameletPropertyField extends React.Component<Props, State> { this.setState({selectIsOpen: false}); } + selectKubernetes = (value: string) => { + const propertyId = this.state.kubernetesSelectorProperty; + if (propertyId){ + if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}"; + this.parametersChanged(propertyId, value); + this.setState({showKubernetesSelector: false, kubernetesSelectorProperty: undefined}) + } + } + + openKubernetesSelector = (propertyName: string) => { + this.setState({kubernetesSelectorProperty: propertyName, showKubernetesSelector: true}); + } + + closeKubernetesSelector = () => { + this.setState({showKubernetesSelector: false}) + } + + getKubernetesSelectorModal() { + return ( + <Modal + title="Select from Kubernetes" + width={'50%'} + className='dsl-modal' + isOpen={this.state.showKubernetesSelector} + onClose={() => this.closeKubernetesSelector()} + actions={{}}> + <KubernetesSelector + dark={false} + onSelect={this.selectKubernetes}/> + </Modal>) + } + getStringInput() { const showEditor = this.state.showEditor; + const showPassword = this.state.showPassword; const property = this.props.property; const value = this.props.value; const prefix = "parameters"; const id = prefix + "-" + property.id; + const noKubeSelectorButton = ["uri", "id", "description", "group"].includes(property.id); + const showKubeSelectorButton = KubernetesAPI.inKubernetes && !showEditor && !noKubeSelectorButton return <InputGroup> + {showKubeSelectorButton && + <Tooltip position="bottom-end" content="Select from Kubernetes"> + <Button variant="control" onClick={e => this.openKubernetesSelector(property.id)}> + <KubernetesIcon/> + </Button> + </Tooltip>} {(!showEditor || property.format === "password") && <TextInput className="text-field" isRequired - type={property.format ? "password" : "text"} + type={property.format && !showPassword ? "password" : "text"} id={id} name={id} value={value} onChange={e => this.parametersChanged(property.id, e)}/>} @@ -83,6 +134,13 @@ export class KameletPropertyField extends React.Component<Props, State> { </Button> </Tooltip> } + {property.format === "password" && + <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}> + <Button variant="control" onClick={e => this.setState({showPassword: !showPassword})}> + {showPassword ? <ShowIcon/> : <HideIcon/>} + </Button> + </Tooltip> + } </InputGroup> } @@ -92,44 +150,47 @@ export class KameletPropertyField extends React.Component<Props, State> { const prefix = "parameters"; const id = prefix + "-" + property.id; return ( - <FormGroup - data-tour={property.id} - key={id} - label={property.title} - fieldId={id} - labelIcon={ - <Popover - position={"left"} - headerContent={property.title} - bodyContent={property.description} - footerContent={ - <div> - {property.default !== undefined && - <div>Default: {property.default.toString()}</div>} - {property.example !== undefined && <div>Example: {property.example}</div>} - </div> - }> - <button type="button" aria-label="More info" onClick={e => e.preventDefault()} - className="pf-c-form__group-label-help"> - <HelpIcon noVerticalAlign/> - </button> - </Popover> - }> - {property.type === 'string' && this.getStringInput() - } - {['integer', 'int', 'number'].includes(property.type) && - <TextInput className="text-field" isRequired type='number' id={id} name={id} value={value} - onChange={e => this.parametersChanged(property.id, Number(e))} - /> - } - {property.type === 'boolean' && <Switch - id={id} name={id} - value={value?.toString()} - aria-label={id} - isChecked={Boolean(value) === true} - onChange={e => this.parametersChanged(property.id, !Boolean(value))}/> - } - </FormGroup> + <div> + <FormGroup + data-tour={property.id} + key={id} + label={property.title} + fieldId={id} + labelIcon={ + <Popover + position={"left"} + headerContent={property.title} + bodyContent={property.description} + footerContent={ + <div> + {property.default !== undefined && + <div>Default: {property.default.toString()}</div>} + {property.example !== undefined && <div>Example: {property.example}</div>} + </div> + }> + <button type="button" aria-label="More info" onClick={e => e.preventDefault()} + className="pf-c-form__group-label-help"> + <HelpIcon noVerticalAlign/> + </button> + </Popover> + }> + {property.type === 'string' && this.getStringInput() + } + {['integer', 'int', 'number'].includes(property.type) && + <TextInput className="text-field" isRequired type='number' id={id} name={id} value={value} + onChange={e => this.parametersChanged(property.id, Number(e))} + /> + } + {property.type === 'boolean' && <Switch + id={id} name={id} + value={value?.toString()} + aria-label={id} + isChecked={Boolean(value) === true} + onChange={e => this.parametersChanged(property.id, !Boolean(value))}/> + } + </FormGroup> + {this.getKubernetesSelectorModal()} + </div> ) } } \ No newline at end of file diff --git a/karavan-designer/src/designer/route/property/KubernetesSelector.tsx b/karavan-designer/src/designer/route/property/KubernetesSelector.tsx new file mode 100644 index 0000000..88c97b7 --- /dev/null +++ b/karavan-designer/src/designer/route/property/KubernetesSelector.tsx @@ -0,0 +1,193 @@ +/* + * 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 from 'react'; +import { + Badge, + Button, + Form, FormGroup, PageSection, + Tab, Tabs, TabTitleText, TextInput, +} from '@patternfly/react-core'; +import '../../karavan.css'; +import {TableComposable, Tbody, Td, Th, Thead, Tr} from "@patternfly/react-table"; +import {KubernetesAPI} from "../../utils/KubernetesAPI"; + +interface Props { + onSelect: (value: string) => void, + dark: boolean, +} + +interface State { + tabIndex: string | number + filter?: string + configMaps: string[] + secrets: string[] + services: string[] +} + +export class KubernetesSelector extends React.Component<Props, State> { + + public state: State = { + tabIndex: "configMap", + configMaps: KubernetesAPI.configMaps, + secrets: KubernetesAPI.secrets, + services: KubernetesAPI.services + }; + + selectTab = (evt: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: string | number) => { + this.setState({tabIndex: eventKey}) + } + + checkFilter = (name: string): boolean => { + if (this.state.filter !== undefined && name) { + return name.toLowerCase().includes(this.state.filter.toLowerCase()) + } else { + return true; + } + } + + searchInput = () => { + return ( + <Form isHorizontal className="search" autoComplete="off"> + <FormGroup fieldId="search"> + <TextInput className="text-field" type="text" id="search" name="search" iconVariant='search' + value={this.state.filter} + onChange={e => this.setState({filter: e})}/> + </FormGroup> + </Form> + ) + } + + render() { + const configMaps = this.state.configMaps; + console.log(configMaps); + const secrets = this.state.secrets; + const services = this.state.services; + console.log(services); + return ( + <PageSection variant={this.props.dark ? "darker" : "light"}> + {this.searchInput()} + <Tabs data-tour="selector-tabs" style={{overflow: 'hidden'}} activeKey={this.state.tabIndex} onSelect={this.selectTab}> + <Tab eventKey={"configMap"} key={"configMap"} title={<TabTitleText>ConfigMaps</TabTitleText>}> + <TableComposable variant='compact' borders={false}> + <Thead> + <Tr> + <Th/> + <Th key='name'>Name</Th> + <Th key='data'>Data</Th> + </Tr> + </Thead> + <Tbody> + {configMaps + .filter(name => this.checkFilter(name)) + .map((name, idx: number) => { + const configMapName = name.split("/")[0]; + const data = name.split("/")[1]; + return ( + <Tr key={name}> + <Td noPadding isActionCell> + <Badge>CM</Badge> + </Td> + <Td noPadding> + {configMapName} + </Td> + <Td noPadding> + <Button style={{padding: '6px'}} variant={"link"} onClick={ + e => this.props.onSelect?.call(this, "configmap:" + name)}> + {data} + </Button> + </Td> + </Tr> + ) + })} + </Tbody> + </TableComposable> + </Tab> + <Tab eventKey={"secret"} key={"secret"} title={<TabTitleText>Secrets</TabTitleText>}> + <TableComposable variant='compact' borders={false}> + <Thead> + <Tr> + <Th/> + <Th key='name'>Name</Th> + <Th key='data'>Data</Th> + </Tr> + </Thead> + <Tbody> + {secrets + .filter(name => this.checkFilter(name)) + .map((name, idx: number) => { + const configMapName = name.split("/")[0]; + const data = name.split("/")[1]; + return ( + <Tr key={name}> + <Td noPadding isActionCell> + <Badge>S</Badge> + </Td> + <Td noPadding> + {configMapName} + </Td> + <Td noPadding> + <Button style={{padding: '6px'}} variant={"link"} onClick={ + e => this.props.onSelect?.call(this, "secret:" + name)}> + {data} + </Button> + </Td> + </Tr> + ) + })} + </Tbody> + </TableComposable> + </Tab> + <Tab eventKey={"service"} key={"service"} title={<TabTitleText>Services</TabTitleText>}> + <TableComposable variant='compact' borders={false}> + <Thead> + <Tr> + <Th/> + <Th key='name'>Name</Th> + <Th key='host'>Host:Port</Th> + </Tr> + </Thead> + <Tbody> + {services + .filter(name => this.checkFilter(name)) + .map((name, idx: number) => { + const serviceName = name.split("|")[0]; + const hostPort = name.split("|")[1]; + return ( + <Tr key={name}> + <Td noPadding isActionCell> + <Badge>S</Badge> + </Td> + <Td noPadding> + {serviceName} + </Td> + <Td noPadding> + <Button style={{padding: '6px'}} variant={"link"} onClick={ + e => this.props.onSelect?.call(this, hostPort)}> + {hostPort} + </Button> + </Td> + </Tr> + ) + })} + </Tbody> + </TableComposable> + </Tab> + </Tabs> + </PageSection> + ); + } +} \ No newline at end of file diff --git a/karavan-designer/src/designer/utils/CamelUi.tsx b/karavan-designer/src/designer/utils/CamelUi.tsx index c421221..661fb7a 100644 --- a/karavan-designer/src/designer/utils/CamelUi.tsx +++ b/karavan-designer/src/designer/utils/CamelUi.tsx @@ -47,10 +47,12 @@ const StepElements: string[] = [ "RecipientListDefinition", "RemoveHeaderDefinition", "RemoveHeadersDefinition", + "RemovePropertyDefinition", "ResequenceDefinition", "SagaDefinition", "SetBodyDefinition", "SetHeaderDefinition", + "SetPropertyDefinition", "SortDefinition", "SplitDefinition", "ThreadsDefinition", @@ -373,10 +375,14 @@ export class CamelUi { return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath d='m24.08924,9.30736l-1.67194,1.57734l4.23986,3.99986l-5.47865,0a5.92883,5.59337 0 0 0 -4.60981,-4.34899l0,-10.15173l-2.36467,0l0,10.15173a5.91168,5.5772 0 0 0 0,10.92886l0,10.15173l2.36467,0l0,-10.15173a5.92883,5. [...] case "RemoveHeaderDefinition": return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath d='m24,30l-20,0a2.0021,2.0021 0 0 1 -2,-2l0,-6a2.0021,2.0021 0 0 1 2,-2l20,0a2.0021,2.0021 0 0 1 2,2l0,6a2.0021,2.0021 0 0 1 -2,2zm-20,-8l-0.0015,0l0.0015,6l20,0l0,-6l-20,0z' id='svg_1'/%3E%3Cpolygon id='svg_2' poi [...] + case "RemovePropertyDefinition": + return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath d='m24,30l-20,0a2.0021,2.0021 0 0 1 -2,-2l0,-6a2.0021,2.0021 0 0 1 2,-2l20,0a2.0021,2.0021 0 0 1 2,2l0,6a2.0021,2.0021 0 0 1 -2,2zm-20,-8l-0.0015,0l0.0015,6l20,0l0,-6l-20,0z' id='svg_1'/%3E%3Cpolygon id='svg_2' poi [...] case "RemoveHeadersDefinition": return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpolygon id='svg_2' points='32.12467002868652,6.015733242034912 32.12467002868652,4.021692276000977 27.047643661499023,4.021692276000977 27.047643661499023,-1.0553351640701294 25.05360221862793,-1.0553350448608398 25.053 [...] case "SetHeaderDefinition": return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath d='m24,30l-20,0a2.0021,2.0021 0 0 1 -2,-2l0,-6a2.0021,2.0021 0 0 1 2,-2l20,0a2.0021,2.0021 0 0 1 2,2l0,6a2.0021,2.0021 0 0 1 -2,2zm-20,-8l-0.0015,0l0.0015,6l20,0l0,-6l-20,0z' id='svg_1'/%3E%3Cpolygon id='svg_2' poi [...] + case "SetPropertyDefinition": + return "data:image/svg+xml,%3Csvg width='32' height='32' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath d='m24,30l-20,0a2.0021,2.0021 0 0 1 -2,-2l0,-6a2.0021,2.0021 0 0 1 2,-2l20,0a2.0021,2.0021 0 0 1 2,2l0,6a2.0021,2.0021 0 0 1 -2,2zm-20,-8l-0.0015,0l0.0015,6l20,0l0,-6l-20,0z' id='svg_1'/%3E%3Cpolygon id='svg_2' poi [...] case "SetBodyDefinition": return "data:image/svg+xml,%3Csvg width='32px' height='32px' viewBox='0 0 32 32' id='icon' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E .cls-1 %7B fill: none; %7D %3C/style%3E%3C/defs%3E%3Cpolygon points='30 24 26 24 26 20 24 20 24 24 20 24 20 26 24 26 24 30 26 30 26 26 30 26 30 24'/%3E%3Cpath d='M16,28H8V4h8v6a2.0058,2.0058,0,0,0,2,2h6v4h2V10a.9092.9092,0,0,0-.3-.7l-7-7A.9087.9087,0,0,0,18,2H8A2.0058,2.0058,0,0,0,6,4V28a2.0058,2.0058,0,0,0,2,2h8ZM18,4.4,23. [...] case "MarshalDefinition": @@ -410,9 +416,9 @@ export class CamelUi { case "ErrorHandlerBuilderRef": return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='top-icon' width='36px' height='36px' viewBox='0 0 36 36' version='1.1' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle class='clr-i-outline clr-i-outline-path-1' cx='18' cy='26.06' r='1.33'%3E%3C/circle%3E%3Cpath class='clr-i-outline clr-i-outline-path-2' d='M18,22.61a1,1,0,0,1-1-1v-12a1,1,0,1,1,2,0v12A1,1,0,0,1,18,22.61Z'%3E%3C/path%3E%3Cpath class='clr-i-outline clr-i-outline-path-3' d='M18,34A1 [...] case "ThrowExceptionDefinition": - return "data:image/svg+xml,%0A%3Csvg width='32px' height='32px' viewBox='0 0 32 32' id='icon' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Ctitle%3Ewarning%3C/title%3E%3Cpath d='M16,2A14,14,0,1,0,30,16,14,14,0,0,0,16,2Zm0,26A12,12,0,1,1,28,16,12,12,0,0,1,16,28Z' transform='translate(0 0)'/%3E%3Crect x='15' y='8' width='2' height='11'/%3E%3Cpath d='M16,22a1.5,1.5,0,1,0,1.5,1.5A1.5,1.5,0,0,0,16,22Z' transform='tran [...] + return "data:image/svg+xml,%3Csvg version='1.1' id='icon' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='32px' height='32px' viewBox='0 0 32 32' style='enable-background:new 0 0 32 32;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill:none;%7D .st1%7Bopacity:0;fill-opacity:0;%7D%0A%3C/style%3E%3Crect id='_Transparent_Rectangle_' class='st0' width='32' height='32'/%3E%3Cpath d='M16,2C8.3,2,2,8.3,2,16s6.3,1 [...] case "OnExceptionDefinition": - return "data:image/svg+xml,%3Csvg version='1.1' id='icon' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='32px' height='32px' viewBox='0 0 32 32' style='enable-background:new 0 0 32 32;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill:none;%7D .st1%7Bopacity:0;fill-opacity:0;%7D%0A%3C/style%3E%3Crect id='Transparent_Rectangle' class='st0' width='32' height='32'/%3E%3Cpath id='Compound_Path' d='M16,2C8.3,2 [...] + return "data:image/svg+xml,%3Csvg version='1.1' id='icon' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='32px' height='32px' viewBox='0 0 32 32' style='enable-background:new 0 0 32 32;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill:none;%7D%0A%3C/style%3E%3Ctitle%3Echeckmark%3C/title%3E%3Cpath d='M16,2C8.2,2,2,8.2,2,16s6.2,14,14,14s14-6.2,14-14S23.8,2,16,2z M16,28C9.4,28,4,22.6,4,16S9.4,4,16,4s12,5.4,1 [...] default: return camelIcon; } @@ -444,7 +450,6 @@ export class CamelUi { static getIconForDslName = (dslName: string):JSX.Element => { switch (dslName) { case 'AggregateDefinition': return <AggregateIcon/>; - case 'AggregateDefinition' :return <AggregateIcon/>; case 'ChoiceDefinition' :return <ChoiceIcon/>; case 'SplitDefinition' :return <SplitIcon/>; case 'SagaDefinition' :return <SagaIcon/>; diff --git a/karavan-designer/src/designer/utils/KubernetesAPI.ts b/karavan-designer/src/designer/utils/KubernetesAPI.ts new file mode 100644 index 0000000..5168bc3 --- /dev/null +++ b/karavan-designer/src/designer/utils/KubernetesAPI.ts @@ -0,0 +1,19 @@ +export class KubernetesAPI { + + static inKubernetes: boolean = false; + static configMaps: string[] = []; + static secrets: string[] = []; + static services: string[] = []; + + static setConfigMaps(configMaps: string[]){ + this.configMaps = configMaps + } + + static setSecrets(secrets: string[]){ + this.secrets = secrets + } + + static setServices(services: string[]){ + this.services = services + } +} \ No newline at end of file