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 cd12854604434f93294fc42a1dd0d450fdaa9607 Author: Marat Gubaidullin <[email protected]> AuthorDate: Sun Mar 1 12:53:44 2026 -0500 Fix Topology for 4.18.0 --- karavan-vscode/webview/App.tsx | 8 +- .../karavan/app/navigation/ProjectFunctionHook.tsx | 196 +++++++++++++++++++++ .../project/project-topology/TopologyTab.tsx | 2 - .../project/project-topology/TopologyToolbar.tsx | 102 ----------- 4 files changed, 201 insertions(+), 107 deletions(-) diff --git a/karavan-vscode/webview/App.tsx b/karavan-vscode/webview/App.tsx index a7e8de3e..db8f4a2b 100644 --- a/karavan-vscode/webview/App.tsx +++ b/karavan-vscode/webview/App.tsx @@ -30,6 +30,8 @@ import { TopologyTab } from "@features/project/project-topology/TopologyTab"; import { DocumentationPage } from "@features/documentation/DocumentationPage"; import { KaravanDesigner } from "@features/project/designer/KaravanDesigner"; import { EventBus } from "@features/project/designer/utils/EventBus"; +import {ProjectFunctionHook} from "@app/navigation/ProjectFunctionHook"; +import {ProjectProvider} from "@features/project/ProjectContext"; interface Props { } @@ -262,9 +264,9 @@ class App extends React.Component<Props, State> { <DocumentationPage /> } {loaded && page === "topology" && - <TopologyTab - asyncApiJson={undefined} - /> + <ProjectProvider useProjectHook={ProjectFunctionHook}> + <TopologyTab asyncApiJson={undefined}/> + </ProjectProvider> } </div> ) diff --git a/karavan-vscode/webview/karavan/app/navigation/ProjectFunctionHook.tsx b/karavan-vscode/webview/karavan/app/navigation/ProjectFunctionHook.tsx new file mode 100644 index 00000000..2f204d7a --- /dev/null +++ b/karavan-vscode/webview/karavan/app/navigation/ProjectFunctionHook.tsx @@ -0,0 +1,196 @@ +/* + * 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 {EventBus} from "@features/project/designer/utils/EventBus"; +import {ProjectFile} from "@models/ProjectModels"; +import {DslMetaModel} from "@features/project/designer/utils/DslMetaModel"; +import {v4 as uuidv4} from "uuid"; +import {CamelUi} from "@features/project/designer/utils/CamelUi"; +import {Integration} from "@karavan-core/model/IntegrationDefinition"; +import {FILE_WORDS_SEPARATOR, KARAVAN_DOT_EXTENSION, KARAVAN_FILENAME, OPENAPI_FILE_NAME_JSON} from "@karavan-core/contants"; +import {RouteConfigurationDefinition} from "@karavan-core/model/CamelDefinition"; +import {CamelDefinitionApiExt} from "@karavan-core/api/CamelDefinitionApiExt"; +import {CamelDefinitionYaml} from "@karavan-core/api/CamelDefinitionYaml"; +import {CamelDefinitionApi} from "@karavan-core/api/CamelDefinitionApi"; +import {useDesignerStore, useSelectorStore} from "@features/project/designer/DesignerStore"; +import {shallow} from "zustand/shallow"; +import {useAppConfigStore, useFilesStore, useFileStore, useProjectStore, useWizardStore} from "@stores/ProjectStore"; +import {toSpecialRouteId} from "@features/project/designer/utils/ValidatorUtils"; + +export function ProjectFunctionHook() { + + const [config] = useAppConfigStore((state) => [state.config], shallow) + const [files] = useFilesStore((s) => [s.files], shallow); + const [isRouteTemplate] = useSelectorStore((s) => [s.isRouteTemplate], shallow) + const [setFile] = useFileStore((s) => [s.setFile], shallow); + const [setShowWizard] = useWizardStore((s) => [s.setShowWizard], shallow) + const [setDesignerSwitch] = useDesignerStore((s) => [s.setDesignerSwitch], shallow) + const [project, tabIndex, setTabIndex, refreshTrace, fetchCamelStatuses] = + useProjectStore((s) => [s.project, s.tabIndex, s.setTabIndex, s.refreshTrace, s.fetchCamelStatuses], shallow); + + function createRouteConfiguration() { + const integration = Integration.createNew(KARAVAN_FILENAME.ROUTE_CONFIGURATION); + const routeConfiguration = new RouteConfigurationDefinition(); + const i = CamelDefinitionApiExt.addRouteConfigurationToIntegration(integration, routeConfiguration); + const yaml = CamelDefinitionYaml.integrationToYaml(i); + const file = new ProjectFile(KARAVAN_FILENAME.ROUTE_CONFIGURATION, project.projectId, yaml, Date.now()); + saveNewFile(file, true, 'routes') + } + + function generateParamUri(dsl: DslMetaModel) { + const uuid = uuidv4().substring(0, 3) + const uri = dsl.uri + FILE_WORDS_SEPARATOR + + (dsl.properties && Object.keys(dsl.properties).length > 0 + ? Object.values(dsl.properties).join(FILE_WORDS_SEPARATOR) + : uuid); + return uri + .replace(/[^a-zA-Z0-9]/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); + } + + function createNewRouteFile(dsl: DslMetaModel, parentId: string, position?: number | undefined, fileName?: string) { + try { + if (dsl.dsl === 'FromDefinition' && dsl.uri) { + const paramsUri = generateParamUri(dsl); + const fullUri = `From${FILE_WORDS_SEPARATOR}${paramsUri}` + + const name = toSpecialRouteId(`from${FILE_WORDS_SEPARATOR}${fullUri}`); + const fName = (fileName != undefined ? fileName : name) + KARAVAN_DOT_EXTENSION.CAMEL_YAML; + const route = CamelUi.createRouteFromComponent(dsl.uri, dsl.properties, ''); + const integration = Integration.createNew(fName); + let i; + if (isRouteTemplate) { + const keys = dsl.properties ? Object.keys(dsl.properties) : []; + const key = keys.at(0); + const routeId = dsl.properties?.[key] + "Route"; + const templateId = dsl.properties?.[key] + "RouteTemplate"; + const route = CamelUi.createRouteFromComponent(dsl.uri, dsl.properties, ''); + route.id = routeId + route.nodePrefixId = routeId + const routeTemplate = CamelDefinitionApi.createRouteTemplateDefinition({id: templateId, route: route}); + i = CamelDefinitionApiExt.addRouteTemplateToIntegration(integration, routeTemplate); + } else { + i = CamelDefinitionApiExt.addStepToIntegration(integration, route, ''); + } + + const yaml = CamelDefinitionYaml.integrationToYaml(i); + const file = new ProjectFile(fName, project.projectId, yaml, Date.now()); + saveNewFile(file, true, 'routes') + } + } catch (e: any) { + console.error(e); + EventBus.sendAlert("Error creating file", e.message, "danger"); + } + } + + function createNewRestFile() { + try { + const fileExists = files.find(f => f.name === "rest-api.camel.yaml") !== undefined; + const uuid = uuidv4().substring(0, 3) + const fileName = 'rest-api' + (fileExists ? FILE_WORDS_SEPARATOR + uuid : '') + '.camel.yaml'; + const nodePrefixId = 'rest-' + uuid; + const rest = CamelDefinitionApi.createRestDefinition({ + id: nodePrefixId, + description: 'Service Example', + path: 'example', + consumes: 'application/json', + produces: 'application/json', + get: [CamelDefinitionApi.createGetDefinition({to: 'direct:getExample', description: 'GET Example'})], + post: [CamelDefinitionApi.createPostDefinition({to: 'direct:postExample', description: 'POST Example'})], + delete: [CamelDefinitionApi.createDeleteDefinition({to: 'direct:deleteExample', description: 'DELETE Example'})], + }); + const restConfiguration = CamelDefinitionApi.createRestConfigurationDefinition({ + inlineRoutes: false, + }); + const integration = Integration.createNew(fileName); + let i = CamelDefinitionApiExt.addRestToIntegration(integration, rest); + i = CamelDefinitionApiExt.addRestToIntegration(i, restConfiguration); + const yaml = CamelDefinitionYaml.integrationToYaml(i); + const file = new ProjectFile(fileName, project.projectId, yaml, Date.now()); + saveNewFile(file, true, 'rest') + } catch (e: any) { + EventBus.sendAlert("Error creating file", e.message, "danger"); + } + } + + function createNewKamelet() { + setFile('create', undefined, 'kamelet') + } + + function createNewBean() { + setShowWizard(true) + } + + function createOpenApiRestFile() { + try { + const fileName = 'openapi-rest-config.camel.yaml'; + const rest = CamelDefinitionApi.createRestDefinition({ + id: 'openApiRestService', + openApi: CamelDefinitionApi.createOpenApiDefinition({specification: 'classpath://' + OPENAPI_FILE_NAME_JSON}) + }); + const restConfiguration = CamelDefinitionApi.createRestConfigurationDefinition({ + inlineRoutes: false, + }); + const integration = Integration.createNew(fileName); + let i = CamelDefinitionApiExt.addRestToIntegration(integration, rest); + i = CamelDefinitionApiExt.addRestToIntegration(i, restConfiguration); + const yaml = CamelDefinitionYaml.integrationToYaml(i); + const file = new ProjectFile(fileName, project.projectId, yaml, Date.now()); + saveNewFile(file, false) + } catch (e: any) { + EventBus.sendAlert("Error creating file", e.message, "danger"); + } + } + + function createOpenApiJsonFile() { + + } + + function createAsyncApi() { + + } + + function createOpenApi() { + + } + + function saveNewFile(file: ProjectFile, openFile: boolean, designerTab?: "routes" | "rest" | "beans" | "kamelet") { + + } + + async function saveFiles(projectId: string, filesToSave: ProjectFile[], afterSuccess?: () => void) { + + // 1. Fetch all existing files for the project once + + } + + function saveFile(file: ProjectFile, afterSuccess?: () => void) { + + } + + function refreshData() { + + } + + function refreshSharedData() { + + } + + return {createNewRouteFile, createOpenApiRestFile, createNewBean, createNewKamelet, createRouteConfiguration, refreshSharedData, + project, createNewRestFile, refreshData, createAsyncApi, createOpenApi} +} diff --git a/karavan-vscode/webview/karavan/features/project/project-topology/TopologyTab.tsx b/karavan-vscode/webview/karavan/features/project/project-topology/TopologyTab.tsx index 9d100619..433ec661 100644 --- a/karavan-vscode/webview/karavan/features/project/project-topology/TopologyTab.tsx +++ b/karavan-vscode/webview/karavan/features/project/project-topology/TopologyTab.tsx @@ -49,7 +49,6 @@ import {useFilesStore, useProjectStore} from "@stores/ProjectStore"; import {NODE_POSITIONED_EVENT} from "@patternfly/react-topology/src/types"; import {OPENAPI_FILE_NAME_JSON} from "@karavan-core/contants"; import {runInAction} from "mobx"; -import {TopologyToolbar} from "@features/project/project-topology/TopologyToolbar"; import {TopologyElkLayout} from "@features/project/project-topology/TopologyElkLayout"; interface Props { @@ -170,7 +169,6 @@ export function TopologyTab(props: Props) { return ( <> - <TopologyToolbar/> <TopologyView className="topology-panel" controlBar={ diff --git a/karavan-vscode/webview/karavan/features/project/project-topology/TopologyToolbar.tsx b/karavan-vscode/webview/karavan/features/project/project-topology/TopologyToolbar.tsx deleted file mode 100644 index ead29fef..00000000 --- a/karavan-vscode/webview/karavan/features/project/project-topology/TopologyToolbar.tsx +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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 * as React from 'react'; -import {MouseEventHandler, ReactElement} from 'react'; -import {Button} from '@patternfly/react-core'; -import {shallow} from "zustand/shallow"; -import {useAppConfigStore} from '@stores/ProjectStore'; -import {useTopologyHook} from '@features/project/project-topology/useTopologyHook'; -import {useRouteDesignerHook} from "@features/project/designer/route/useRouteDesignerHook"; -import {APPLICATION_PROPERTIES, DOCKER_COMPOSE, DOCKER_STACK} from "@models/ProjectModels"; -import {ProjectTitle} from "@features/project/ProjectTitle"; -import {useProjectFunctions} from "@features/project/ProjectContext"; -import {AddLarge} from "@carbon/icons-react"; - -export function TopologyToolbar() { - - const [config] = useAppConfigStore((s) => [s.config], shallow); - const isDev = config.environment === 'dev'; - - const { - createNewBean, - createRouteConfiguration, - createOpenApi, - createNewKamelet, - project - } = useProjectFunctions(); - const {openSelector} = useRouteDesignerHook(); - - const {selectFile} = useTopologyHook(); - - function getInfraButton(): ReactElement { - const isKubernetes = config.infrastructure === 'kubernetes'; - const swarmMode = config.swarmMode; - const fileName = isKubernetes - ? 'deployment.jkube.yaml' - : (swarmMode ? DOCKER_STACK : DOCKER_COMPOSE); - - return ( - <div> - <Button variant={"tertiary"} - className='bean-button' - // icon={<OutlinedFileAltIcon/>} - onClick={() => { - selectFile(fileName) - }} - > - {isKubernetes ? "Deployment" : "Compose"} - </Button> - </div> - ) - } - - function getButton(caption: string, - variant?: 'primary' | 'secondary' | 'tertiary' | 'danger' | 'warning' | 'link' | 'plain' | 'control' | 'stateful', - icon?: ReactElement, - onClick?: MouseEventHandler<any> | undefined): ReactElement { - return ( - <div> - <Button className="dev-action-button " - isDisabled={!isDev} - icon={icon} - variant={variant} - onClick={onClick} - > - {caption} - </Button> - </div> - ) - } - - - return ( - <div className='topology-toolbar'> - <div className="group-switch"> - <ProjectTitle/> - </div> - - {getButton("Route", 'primary', <AddLarge className='carbon'/>, event => openSelector(undefined, undefined))} - {getButton("Route Configuration", 'secondary', <AddLarge className='carbon'/>, event => createRouteConfiguration())} - {getButton("Route Template", 'secondary', <AddLarge className='carbon'/>, event => openSelector(undefined, undefined, true, undefined, true))} - {getButton("Kamelet", 'tertiary', <AddLarge className='carbon'/>, event => createNewKamelet())} - {getButton("Bean", 'tertiary', <AddLarge className='carbon'/>, event => createNewBean())} - {getButton("Properties", 'tertiary', undefined, event => selectFile(APPLICATION_PROPERTIES))} - {getInfraButton()} - </div> - ) -} \ No newline at end of file
