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 b1584059 Fix File Upload for #885 b1584059 is described below commit b15840595c2506d0c5f91b0fa0f3d9108256d2a7 Author: Marat Gubaidullin <ma...@talismancloud.io> AuthorDate: Mon Sep 18 13:54:13 2023 -0400 Fix File Upload for #885 --- .../src/main/webui/src/api/ProjectModels.ts | 5 +- .../src/main/webui/src/designer/utils/CamelUi.tsx | 2 +- .../src/main/webui/src/project/file/FileEditor.tsx | 2 +- .../src/main/webui/src/project/files/FilesTab.tsx | 2 +- .../webui/src/project/files/UploadFileModal.tsx | 248 ++++++++++----------- 5 files changed, 128 insertions(+), 131 deletions(-) diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts index 0a401677..97373e2d 100644 --- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts +++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectModels.ts @@ -132,6 +132,8 @@ export const ProjectFileTypes: ProjectFileType[] = [ new ProjectFileType("YAML", "YAML", "yaml"), new ProjectFileType("LOG", "Log", "log"), new ProjectFileType("SH", "Script", "sh"), + new ProjectFileType("SQL", "SQL", "sql"), + new ProjectFileType("OTHER", "Other", "*"), ]; @@ -141,6 +143,7 @@ export function getProjectFileType (file: ProjectFile) { if (file.name.endsWith(".json")) return ProjectFileTypes.filter(p => p.name === "JSON").map(p => p.title)[0]; if (file.name.endsWith(".yaml")) return ProjectFileTypes.filter(p => p.name === "YAML").map(p => p.title)[0]; const extension = file.name.substring(file.name.lastIndexOf('.') + 1); - return ProjectFileTypes.filter(p => p.extension === extension).map(p => p.title)[0]; + const types = ProjectFileTypes.filter(p => p.extension === extension); + return types.length >0 ? types.map(p => p.title)[0] : "Other"; } diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx index 66e9b1e8..6b3f7093 100644 --- a/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx @@ -289,7 +289,7 @@ export class CamelUi { } static nameFromTitle = (title: string): string => { - return title.replace(/[^0-9a-zA-Z.]+/gi, "-").toLowerCase(); + return title.replace(/[^a-z0-9+]+/gi, "-").toLowerCase(); } static javaNameFromTitle = (title: string): string => { diff --git a/karavan-web/karavan-app/src/main/webui/src/project/file/FileEditor.tsx b/karavan-web/karavan-app/src/main/webui/src/project/file/FileEditor.tsx index 9745f2bf..8f3e0789 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/file/FileEditor.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/file/FileEditor.tsx @@ -90,7 +90,7 @@ export function FileEditor (props: Props) { const isScript = file !== undefined && file.name.endsWith("sh"); const isCode = file !== undefined && (file.name.endsWith("java") || file.name.endsWith("groovy") || file.name.endsWith("json")); const showDesigner = isYaml && isIntegration; - const showEditor = isCode || (isYaml && !isIntegration) || (isYaml) || isScript; + const showEditor = !showDesigner; return ( <> {showDesigner && getDesigner()} diff --git a/karavan-web/karavan-app/src/main/webui/src/project/files/FilesTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/files/FilesTab.tsx index a78470e1..11b6d965 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/files/FilesTab.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/files/FilesTab.tsx @@ -145,7 +145,7 @@ export function FilesTab () { </Table> </div> <CreateFileModal types={types}/> - <UploadFileModal projectId={project.projectId} isOpen={operation === 'upload'} /> + <UploadFileModal projectId={project.projectId}/> <DeleteFileModal /> </PageSection> ) diff --git a/karavan-web/karavan-app/src/main/webui/src/project/files/UploadFileModal.tsx b/karavan-web/karavan-app/src/main/webui/src/project/files/UploadFileModal.tsx index 39620879..9c43b822 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/files/UploadFileModal.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/files/UploadFileModal.tsx @@ -14,7 +14,7 @@ * 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 { TextInput, Button, Modal, FormGroup, ModalVariant, Switch, Form, FileUpload, Radio @@ -23,158 +23,152 @@ import '../../designer/karavan.css'; import {ProjectFile} from "../../api/ProjectModels"; import {KaravanApi} from "../../api/KaravanApi"; import {useFileStore} from "../../api/ProjectStore"; -import {ProjectEventBus} from "../../api/ProjectEventBus"; import {Accept, DropEvent, FileRejection} from "react-dropzone"; import {EventBus} from "../../designer/utils/EventBus"; +import {shallow} from "zustand/shallow"; +import {ProjectService} from "../../api/ProjectService"; interface Props { projectId: string, - isOpen: boolean, } -interface State { - type: 'integration' | 'openapi' - data: string - filename: string - integrationName: string - isLoading: boolean - isRejected: boolean - generateRest: boolean - generateRoutes: boolean -} +export function UploadFileModal(props: Props) { -export class UploadFileModal extends React.Component<Props, State> { + const [operation, setFile] = useFileStore((s) => [s.operation, s.setFile], shallow); + const [type, setType] = useState<'integration' | 'openapi' | 'other'>('integration'); + const [filename, setFilename] = useState(''); + const [integrationName, setIntegrationName] = useState(''); + const [data, setData] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const [isRejected, setIsRejected] = useState(false); + const [generateRest, setGenerateRest] = useState(true); + const [generateRoutes, setGenerateRoutes] = useState(true); - public state: State = { - type: 'integration', - data: '', - filename: '', - integrationName: '', - isLoading: false, - isRejected: false, - generateRest: true, - generateRoutes: true - }; + useEffect(() => { + setFilename('') + setData('') + setType('integration') + }, []); - closeModal () { - useFileStore.setState({operation:"none"}); + function closeModal () { + setFile("none") } - saveAndCloseModal () { - const state = this.state; - const file = new ProjectFile(state.filename, this.props.projectId, state.data, Date.now()); - if (this.state.type === "integration"){ - KaravanApi.postProjectFile(file, res => { + function saveAndCloseModal () { + const file = new ProjectFile(filename, props.projectId, data, Date.now()); + console.log(file); + if (type === "openapi"){ + KaravanApi.postOpenApi(file, generateRest, generateRoutes, integrationName, res => { if (res.status === 200) { - //TODO show notification - this.closeModal(); + EventBus.sendAlert("File uploaded", "", "info") + closeModal(); + ProjectService.refreshProjectData(props.projectId); } else { - this.closeModal(); + closeModal(); EventBus.sendAlert("Error", res.statusText, "warning") } }) } else { - KaravanApi.postOpenApi(file, state.generateRest, state.generateRoutes, state.integrationName, res => { + KaravanApi.postProjectFile(file, res => { if (res.status === 200) { - console.log(res) //TODO show notification - this.closeModal(); + EventBus.sendAlert("File uploaded", "", "info") + closeModal(); + ProjectService.refreshProjectData(props.projectId); } else { - this.closeModal(); + closeModal(); EventBus.sendAlert("Error", res.statusText, "warning") } }) } } - handleFileInputChange = (file: File) => this.setState({filename: file.name}); - handleFileReadStarted = (fileHandle: File) => this.setState({isLoading: true}); - handleFileReadFinished = (fileHandle: File) => this.setState({isLoading: false}); - handleTextOrDataChange = (data: string) => this.setState({data: data}); - handleFileRejected = (fileRejections: FileRejection[], event: DropEvent) => this.setState({isRejected: true}); - handleClear = (event: React.MouseEvent<HTMLButtonElement>) => this.setState({ - filename: '', - data: '', - isRejected: false - }); + const handleFileInputChange = (file: File) => setFilename(file.name); + const handleFileReadStarted = (fileHandle: File) => setIsLoading(true); + const handleFileReadFinished = (fileHandle: File) => setIsLoading(false); + const handleTextOrDataChange = (data: string) => setData(data); + const handleFileRejected = (fileRejections: FileRejection[], event: DropEvent) => setIsRejected(true); + const handleClear = (event: React.MouseEvent<HTMLButtonElement>) => { + setFilename(''); + setData(''); + setIsRejected(false); + }; - render() { - const fileNotUploaded = (this.state.filename === '' || this.state.data === ''); - const isDisabled = this.state.type === 'integration' - ? fileNotUploaded - : !(!fileNotUploaded && this.state.integrationName !== undefined && this.state.integrationName.endsWith(".yaml")); - const accept : Accept = this.state.type === 'integration' - ? {'application/yaml': ['.yaml', '.yml']} - : {'application/yaml': ['.yaml', '.yml'], 'application/json': ['.json'], 'plain/text': ['.sql']}; - return ( - <Modal - title="Upload" - variant={ModalVariant.small} - isOpen={this.props.isOpen} - onClose={this.closeModal} - actions={[ - <Button key="confirm" variant="primary" onClick={this.saveAndCloseModal} isDisabled={isDisabled}>Save</Button>, - <Button key="cancel" variant="secondary" onClick={this.closeModal}>Cancel</Button> - ]} - > - <Form> - <FormGroup fieldId="type"> - <Radio value="Integration" label="Integration yaml" name="Integration" id="Integration" isChecked={this.state.type === 'integration'} - onChange={(event, _) => this.setState({ type: _ ? 'integration': 'openapi' })} - />{' '} - <Radio value="OpenAPI" label="OpenAPI json/yaml" name="OpenAPI" id="OpenAPI" isChecked={this.state.type === 'openapi'} - onChange={(event, _) => this.setState({ type: _ ? 'openapi' : 'integration' })} - /> - </FormGroup> - <FormGroup fieldId="upload"> - <FileUpload - id="file-upload" - value={this.state.data} - filename={this.state.filename} - type="text" - hideDefaultPreview - browseButtonText="Upload" - isLoading={this.state.isLoading} - onFileInputChange={(_event, fileHandle: File) => this.handleFileInputChange(fileHandle)} - onDataChange={(_event, data) => this.handleTextOrDataChange(data)} - onTextChange={(_event, text) => this.handleTextOrDataChange(text)} - onReadStarted={(_event, fileHandle: File) => this.handleFileReadStarted(fileHandle)} - onReadFinished={(_event, fileHandle: File) => this.handleFileReadFinished(fileHandle)} - allowEditingUploadedText={false} - onClearClick={this.handleClear} - dropzoneProps={{accept: accept, onDropRejected: this.handleFileRejected}} - validated={this.state.isRejected ? 'error' : 'default'} - /> - </FormGroup> - {this.state.type === 'openapi' && <FormGroup fieldId="generateRest"> - <Switch - id="generate-rest" - label="Generate REST DSL" - labelOff="Do not generate REST DSL" - isChecked={this.state.generateRest} - onChange={(_, checked) => this.setState({generateRest: checked})} - /> - </FormGroup>} - {this.state.type === 'openapi' && this.state.generateRest && <FormGroup fieldId="generateRoutes"> - <Switch - id="generate-routes" - label="Generate Routes" - labelOff="Do not generate Routes" - isChecked={this.state.generateRoutes} - onChange={(_, checked) => this.setState({generateRoutes: checked})} - /> - </FormGroup>} - {this.state.type === 'openapi' && this.state.generateRest && <FormGroup fieldId="integrationName" label="Integration name"> - <TextInput autoComplete="off" - id="integrationName" - type="text" - placeholder="Integration file name with yaml extension" - required - onChange={(_, value) => this.setState({integrationName: value})} - /> - </FormGroup>} - </Form> - </Modal> - ) - } + const fileNotUploaded = (filename === '' || data === ''); + const accept : Accept = type === 'integration' + ? {'application/yaml': ['.yaml', '.yml']} + : {'application/yaml': ['.yaml', '.yml'], 'application/json': ['.json'], 'plain/text': ['.sql']}; + return ( + <Modal + title="Upload" + variant={ModalVariant.small} + isOpen={operation === 'upload'} + onClose={closeModal} + actions={[ + <Button key="confirm" variant="primary" onClick={saveAndCloseModal} isDisabled={fileNotUploaded}>Save</Button>, + <Button key="cancel" variant="secondary" onClick={closeModal}>Cancel</Button> + ]} + > + <Form> + <FormGroup fieldId="type"> + <Radio value="Integration" label="Integration yaml" name="Integration" id="Integration" isChecked={type === 'integration'} + onChange={(event, _) => setType(_ ? 'integration': 'openapi' )} + />{' '} + <Radio value="OpenAPI" label="OpenAPI json/yaml" name="OpenAPI" id="OpenAPI" isChecked={type === 'openapi'} + onChange={(event, _) => setType( _ ? 'openapi' : 'integration' )} + /> + <Radio value="Other" label="Other" name="Other" id="Other" isChecked={type === 'other'} + onChange={(event, _) => setType( _ ? 'other' : 'integration' )} + /> + </FormGroup> + <FormGroup fieldId="upload"> + <FileUpload + id="file-upload" + value={data} + filename={filename} + type="text" + hideDefaultPreview + browseButtonText="Upload" + isLoading={isLoading} + onFileInputChange={(_event, fileHandle: File) => handleFileInputChange(fileHandle)} + onDataChange={(_event, data) => handleTextOrDataChange(data)} + onTextChange={(_event, text) => handleTextOrDataChange(text)} + onReadStarted={(_event, fileHandle: File) => handleFileReadStarted(fileHandle)} + onReadFinished={(_event, fileHandle: File) => handleFileReadFinished(fileHandle)} + allowEditingUploadedText={false} + onClearClick={handleClear} + dropzoneProps={{accept: accept, onDropRejected: handleFileRejected}} + validated={isRejected ? 'error' : 'default'} + /> + </FormGroup> + {type === 'openapi' && <FormGroup fieldId="generateRest"> + <Switch + id="generate-rest" + label="Generate REST DSL" + labelOff="Do not generate REST DSL" + isChecked={generateRest} + onChange={(_, checked) => setGenerateRest(checked)} + /> + </FormGroup>} + {type === 'openapi' && generateRest && <FormGroup fieldId="generateRoutes"> + <Switch + id="generate-routes" + label="Generate Routes" + labelOff="Do not generate Routes" + isChecked={generateRoutes} + onChange={(_, checked) => setGenerateRoutes(checked)} + /> + </FormGroup>} + {type === 'openapi' && generateRest && <FormGroup fieldId="integrationName" label="Integration name"> + <TextInput autoComplete="off" + id="integrationName" + type="text" + placeholder="Integration file name with yaml extension" + required + onChange={(_, value) => setIntegrationName(value)} + /> + </FormGroup>} + </Form> + </Modal> + ) }; \ No newline at end of file