You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2024/01/29 14:42:29 UTC
(camel-karavan) branch main updated: Import file validation (#1085)
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 da5a7ddb Import file validation (#1085)
da5a7ddb is described below
commit da5a7ddb8a5bbc623e8c3a7fd9117d8abab013a0
Author: Mario Volf <mv...@users.noreply.github.com>
AuthorDate: Mon Jan 29 15:42:24 2024 +0100
Import file validation (#1085)
* mvolf - Validate file existence on file import. Use the same validation on file create. Fix for case when email doesn't exist. Fix for quarkus quinoa properties after upgrade.
* mvolf - Revert changes to TopologyToolbar made by build
---
.../org/apache/camel/karavan/git/GitService.java | 7 +-
.../karavan/infinispan/InfinispanService.java | 7 ++
.../project/ProjectFileCreateValidator.java | 35 ++++++
.../src/main/resources/application.properties | 3 +-
.../src/main/webui/src/api/KaravanApi.tsx | 15 ++-
.../src/main/webui/src/api/ProjectService.ts | 17 ++-
.../webui/src/project/files/CreateFileModal.tsx | 131 +++++++++++++++------
.../webui/src/project/files/UploadFileModal.tsx | 130 ++++++++++++++------
8 files changed, 253 insertions(+), 92 deletions(-)
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/git/GitService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/git/GitService.java
index da93a157..25e37750 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/git/GitService.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/git/GitService.java
@@ -309,11 +309,14 @@ public class GitService {
}
private PersonIdent getPersonIdent() {
+ String defaultEmailAddress = "karavan@test.org";
+
if (securityIdentity != null && securityIdentity.getAttributes().get("userinfo") != null) {
UserInfo userInfo = (UserInfo) securityIdentity.getAttributes().get("userinfo");
- return new PersonIdent(securityIdentity.getPrincipal().getName(), userInfo.getEmail());
+ String email = Objects.isNull(userInfo.getEmail()) || userInfo.getEmail().isBlank() ? defaultEmailAddress : userInfo.getEmail();
+ return new PersonIdent(securityIdentity.getPrincipal().getName(), email);
}
- return new PersonIdent("karavan", "karavan@test.org");
+ return new PersonIdent("karavan", defaultEmailAddress);
}
public Git init(String dir, String uri, String branch) throws GitAPIException, IOException, URISyntaxException {
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java
index fca6c20e..594dde7b 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java
@@ -17,8 +17,10 @@
package org.apache.camel.karavan.infinispan;
import jakarta.enterprise.inject.Default;
+import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import org.apache.camel.karavan.infinispan.model.*;
+import org.apache.camel.karavan.validation.project.ProjectFileCreateValidator;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.faulttolerance.Retry;
import org.eclipse.microprofile.health.HealthCheck;
@@ -63,6 +65,9 @@ public class InfinispanService implements HealthCheck {
@ConfigProperty(name = "karavan.infinispan.password")
String infinispanPassword;
+ @Inject
+ ProjectFileCreateValidator projectFileCreateValidator;
+
private RemoteCache<GroupedKey, Project> projects;
private RemoteCache<GroupedKey, ProjectFile> files;
private RemoteCache<GroupedKey, DeploymentStatus> deploymentStatuses;
@@ -174,6 +179,8 @@ public class InfinispanService implements HealthCheck {
}
public void saveProjectFile(ProjectFile file) {
+ projectFileCreateValidator.validate(file).failOnError();
+
files.put(GroupedKey.create(file.getProjectId(), DEFAULT_ENVIRONMENT, file.getName()), file);
}
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/validation/project/ProjectFileCreateValidator.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/validation/project/ProjectFileCreateValidator.java
new file mode 100644
index 00000000..686c3db1
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/validation/project/ProjectFileCreateValidator.java
@@ -0,0 +1,35 @@
+package org.apache.camel.karavan.validation.project;
+
+import java.util.List;
+
+import org.apache.camel.karavan.infinispan.InfinispanService;
+import org.apache.camel.karavan.infinispan.model.ProjectFile;
+import org.apache.camel.karavan.shared.validation.SimpleValidator;
+import org.apache.camel.karavan.shared.validation.ValidationError;
+import org.apache.camel.karavan.shared.validation.Validator;
+
+import jakarta.enterprise.context.ApplicationScoped;
+
+@ApplicationScoped
+public class ProjectFileCreateValidator extends Validator<ProjectFile> {
+
+ private final SimpleValidator simpleValidator;
+ private final InfinispanService infinispanService;
+
+ public ProjectFileCreateValidator(SimpleValidator simpleValidator, InfinispanService infinispanService) {
+ this.simpleValidator = simpleValidator;
+ this.infinispanService = infinispanService;
+ }
+
+
+ @Override
+ protected void validationRules(ProjectFile value, List<ValidationError> errors) {
+ simpleValidator.validate(value, errors);
+
+ boolean projectFileExists = infinispanService.getProjectFile(value.getProjectId(), value.getName()) != null;
+
+ if(projectFileExists) {
+ errors.add(new ValidationError("name", "File with given name already exists"));
+ }
+ }
+}
diff --git a/karavan-web/karavan-app/src/main/resources/application.properties b/karavan-web/karavan-app/src/main/resources/application.properties
index 553da3ce..2efec5fd 100644
--- a/karavan-web/karavan-app/src/main/resources/application.properties
+++ b/karavan-web/karavan-app/src/main/resources/application.properties
@@ -145,8 +145,9 @@ quarkus.kubernetes-client.devservices.enabled=false
quarkus.swagger-ui.always-include=true
-quarkus.quinoa.frozen-lockfile=false
+quarkus.quinoa.ci=false
quarkus.quinoa.package-manager-install=false
quarkus.quinoa.package-manager-install.node-version=18.12.1
quarkus.quinoa.dev-server.port=3003
quarkus.quinoa.dev-server.check-timeout=60000
+quarkus.quinoa.ignored-path-prefixes=/api,/public
diff --git a/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx b/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx
index 95ad0698..840e0234 100644
--- a/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx
@@ -267,12 +267,16 @@ export class KaravanApi {
});
}
+ static async saveProjectFile(file: ProjectFile) {
+ return instance.post('/api/file', file);
+ }
+
static async postProjectFile(file: ProjectFile, after: (res: AxiosResponse<any>) => void) {
instance.post('/api/file', file)
.then(res => {
after(res);
}).catch(err => {
- after(err);
+ after(err);
});
}
@@ -621,14 +625,9 @@ export class KaravanApi {
});
}
- static async postOpenApi(file: ProjectFile, generateRest: boolean, generateRoutes: boolean, integrationName: string, after: (res: AxiosResponse<any>) => void) {
+ static async postOpenApi(file: ProjectFile, generateRest: boolean, generateRoutes: boolean, integrationName: string) {
const uri = `/api/file/openapi/${generateRest}/${generateRoutes}/${integrationName}`;
- instance.post(encodeURI(uri), file)
- .then(res => {
- after(res);
- }).catch(err => {
- after(err);
- });
+ return instance.post(encodeURI(uri), file);
}
static async fetchData(type: 'container' | 'build' | 'none', podName: string, controller: AbortController) {
diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
index b3d925d8..d0d03034 100644
--- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
@@ -245,15 +245,14 @@ export class ProjectService {
return result.data;
}
- public static createFile(file: ProjectFile) {
- KaravanApi.postProjectFile(file, res => {
- if (res.status === 200) {
- // console.log(res) //TODO show notification
- ProjectService.refreshProjectData(file.projectId);
- } else {
- // console.log(res) //TODO show notification
- }
- })
+ public static async createFile(file: ProjectFile) {
+ const result = await KaravanApi.saveProjectFile(file);
+ return result.data;
+ }
+
+ public static async createOpenApiFile(file: ProjectFile, generateRest: boolean, generateRoutes: boolean, integrationName: string) {
+ const result = await KaravanApi.postOpenApi(file, generateRest, generateRoutes, integrationName);
+ return result.data;
}
public static deleteFile(file: ProjectFile) {
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx b/karavan-web/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx
index c5c99037..8c08be7f 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx
@@ -22,12 +22,12 @@ import {
FormGroup,
ModalVariant,
Form,
- ToggleGroupItem, ToggleGroup, FormHelperText, HelperText, HelperTextItem, TextInput,
+ ToggleGroupItem, ToggleGroup, TextInput, Alert, Divider, Grid, Text,
} from '@patternfly/react-core';
import '../../designer/karavan.css';
import {Integration, KameletTypes, MetadataLabels} from "karavan-core/lib/model/IntegrationDefinition";
import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
-import {useFilesStore, useFileStore, useProjectStore} from "../../api/ProjectStore";
+import {useFileStore, useProjectStore} from "../../api/ProjectStore";
import {ProjectFile, ProjectFileTypes} from "../../api/ProjectModels";
import {CamelUi} from "../../designer/utils/CamelUi";
import {ProjectService} from "../../api/ProjectService";
@@ -35,6 +35,12 @@ import {shallow} from "zustand/shallow";
import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
import {KameletApi} from "karavan-core/lib/api/KameletApi";
import {TypeaheadSelect, Value} from "../../designer/ui/TypeaheadSelect";
+import * as yup from "yup";
+import {useForm} from "react-hook-form";
+import {yupResolver} from "@hookform/resolvers/yup";
+import {useResponseErrorHandler} from "../../shared/error/UseResponseErrorHandler";
+import {EventBus} from "../../designer/utils/EventBus";
+import {AxiosError} from "axios";
interface Props {
types: string[],
@@ -43,14 +49,44 @@ interface Props {
export function CreateFileModal(props: Props) {
+ const formValidationSchema = yup.object().shape({
+ name: yup
+ .string()
+ .required("File name is required"),
+ });
+
+ const defaultFormValues = {
+ name: ""
+ };
+
+ const responseToFormErrorFields = new Map<string, string>([
+ ["name", "name"]
+ ]);
+
+ const {
+ register,
+ setError,
+ handleSubmit,
+ formState: { errors },
+ reset,
+ clearErrors
+ } = useForm({
+ resolver: yupResolver(formValidationSchema),
+ mode: "onChange",
+ defaultValues: defaultFormValues
+ });
+
+
const [project] = useProjectStore((s) => [s.project], shallow);
- const [files] = useFilesStore((s) => [s.files], shallow);
const [operation, setFile, designerTab] = useFileStore((s) => [s.operation, s.setFile, s.designerTab], shallow);
const [name, setName] = useState<string>('');
- const [nameAvailable, setNameAvailable] = useState<boolean>(true);
const [fileType, setFileType] = useState<string>();
const [kameletType, setKameletType] = useState<KameletTypes>('source');
const [selectedKamelet, setSelectedKamelet] = useState<string>();
+ const [globalErrors, registerResponseErrors, resetGlobalErrors] = useResponseErrorHandler(
+ responseToFormErrorFields,
+ setError
+ );
useEffect(() => {
if (props.types.length > 0) {
@@ -58,14 +94,44 @@ export function CreateFileModal(props: Props) {
}
}, [props]);
- function cleanValues() {
- setName("");
+ function resetForm() {
+ resetGlobalErrors();
+ reset(defaultFormValues);
+ setName("")
setFileType(props.types.at(0) || 'INTEGRATION');
}
function closeModal() {
setFile("none");
- cleanValues();
+ resetForm();
+ }
+
+ function handleFormSubmit() {
+ const code = getCode();
+ const fullFileName = getFullFileName(name, fileType);
+ const file = new ProjectFile(fullFileName, project.projectId, code, Date.now());
+
+ return ProjectService.createFile(file)
+ .then(() => handleOnFormSubmitSuccess(code, file))
+ .catch((error) => handleOnFormSubmitFailure(error));
+ }
+
+ function handleOnFormSubmitSuccess (code: string, file: ProjectFile) {
+ const message = "File successfully created.";
+ EventBus.sendAlert( "Success", message, "success");
+
+ ProjectService.refreshProjectData(file.projectId);
+
+ resetForm();
+ if (code) {
+ setFile('select', file, designerTab);
+ } else {
+ setFile("none");
+ }
+ }
+
+ function handleOnFormSubmitFailure(error: AxiosError) {
+ registerResponseErrors(error);
}
function getCode(): string {
@@ -92,23 +158,6 @@ export function CreateFileModal(props: Props) {
}
}
- function confirmAndCloseModal() {
- const code = getCode();
- const fullFileName = getFullFileName(name, fileType);
- const file = new ProjectFile(fullFileName, project.projectId, code, Date.now());
- ProjectService.createFile(file);
- cleanValues();
- if (code) {
- setFile('select', file, designerTab);
- } else {
- setFile("none");
- }
- }
-
- function getExistingFilenames(): string[] {
- return files.map(f => f.name);
- }
-
function fileNameCheck(title: string) {
return title.replace(/[^0-9a-zA-Z.]+/gi, "-").toLowerCase();
}
@@ -133,8 +182,6 @@ export function CreateFileModal(props: Props) {
function update(value: string, type?: string) {
setName(value);
- const exists = getExistingFilenames().findIndex(f => f === getFullFileName(value, type)) === -1;
- setNameAvailable(exists);
setFileType(type);
}
@@ -145,9 +192,8 @@ export function CreateFileModal(props: Props) {
isOpen={["create", "copy"].includes(operation)}
onClose={closeModal}
actions={[
- <Button key="confirm" variant="primary" isDisabled={!nameAvailable || name === undefined || name.trim().length === 0}
- onClick={event => confirmAndCloseModal()}>Save</Button>,
- <Button key="cancel" variant="secondary" onClick={event => closeModal()}>Cancel</Button>
+ <Button key="confirm" variant="primary" onClick={handleSubmit(handleFormSubmit)}>Save</Button>,
+ <Button key="cancel" variant="secondary" onClick={closeModal}>Cancel</Button>
]}
>
<Form autoComplete="off" isHorizontal className="create-file-form">
@@ -159,6 +205,8 @@ export function CreateFileModal(props: Props) {
return <ToggleGroupItem key={title} text={title} buttonId={p.name}
isSelected={fileType === p.name}
onChange={(_, selected) => {
+ resetGlobalErrors();
+ clearErrors('name');
update(name, p.name);
}}/>
})}
@@ -178,23 +226,30 @@ export function CreateFileModal(props: Props) {
</ToggleGroup>
</FormGroup>}
<FormGroup label="Name" fieldId="name" isRequired>
- <TextInput id="name"
+ <TextInput className="text-field" type="text" id="name"
aria-label="name"
value={name}
- onChange={(_, value) => update(value, fileType)}/>
- <FormHelperText>
- <HelperText id="helper-text1">
- <HelperTextItem variant={nameAvailable ? 'default' : 'error'}>
- {!nameAvailable ? 'File ': ''}{getFullFileName(name, fileType)}{!nameAvailable ? ' already exists': ''}
- </HelperTextItem>
- </HelperText>
- </FormHelperText>
+ validated={!!errors.name ? 'error' : 'default'}
+ {...register('name')}
+ onChange={(e, value) => {
+ update(value, fileType);
+ register('name').onChange(e);
+ }}
+ />
+ {!!errors.name && <Text style={{ color: 'red', fontStyle: 'italic'}}>{errors?.name?.message}</Text>}
</FormGroup>
{isKamelet && <FormGroup label="Copy from" fieldId="kamelet">
<TypeaheadSelect listOfValues={listOfValues} onSelect={value => {
setSelectedKamelet(value)
}}/>
</FormGroup>}
+ <Grid>
+ {globalErrors &&
+ globalErrors.map((error) => (
+ <Alert title={error} key={error} variant="danger"></Alert>
+ ))}
+ <Divider role="presentation" />
+ </Grid>
</Form>
</Modal>
)
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 dfe1a198..70ba3715 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
@@ -17,16 +17,18 @@
import React, {useEffect, useState} from 'react';
import {
TextInput,
- Button, Modal, FormGroup, ModalVariant, Switch, Form, FileUpload, Radio
+ Button, Modal, FormGroup, ModalVariant, Switch, Form, FileUpload, Radio, Alert, Divider, Grid, Text
} from '@patternfly/react-core';
import '../../designer/karavan.css';
import {ProjectFile} from "../../api/ProjectModels";
-import {KaravanApi} from "../../api/KaravanApi";
import {useFileStore} from "../../api/ProjectStore";
import {Accept, DropEvent, FileRejection} from "react-dropzone";
import {EventBus} from "../../designer/utils/EventBus";
import {shallow} from "zustand/shallow";
import {ProjectService} from "../../api/ProjectService";
+import {useForm} from "react-hook-form";
+import {useResponseErrorHandler} from "../../shared/error/UseResponseErrorHandler";
+import {AxiosError} from "axios";
interface Props {
projectId: string,
@@ -34,6 +36,26 @@ interface Props {
export function UploadFileModal(props: Props) {
+ const defaultFormValues = {
+ upload: ""
+ };
+
+ const responseToFormErrorFields = new Map<string, string>([
+ ["name", "upload"]
+ ]);
+
+ const {
+ register,
+ setError,
+ handleSubmit,
+ formState: { errors },
+ reset,
+ clearErrors
+ } = useForm({
+ mode: "onChange",
+ defaultValues: defaultFormValues
+ });
+
const [operation, setFile] = useFileStore((s) => [s.operation, s.setFile], shallow);
const [type, setType] = useState<'integration' | 'openapi' | 'other'>('integration');
const [filename, setFilename] = useState('');
@@ -43,6 +65,10 @@ export function UploadFileModal(props: Props) {
const [isRejected, setIsRejected] = useState(false);
const [generateRest, setGenerateRest] = useState(true);
const [generateRoutes, setGenerateRoutes] = useState(true);
+ const [globalErrors, registerResponseErrors, resetGlobalErrors] = useResponseErrorHandler(
+ responseToFormErrorFields,
+ setError
+ );
useEffect(() => {
setFilename('')
@@ -50,37 +76,41 @@ export function UploadFileModal(props: Props) {
setType('integration')
}, []);
+ function resetForm() {
+ resetGlobalErrors();
+ }
+
function closeModal () {
- setFile("none")
+ setFile("none");
+ resetForm();
}
- function saveAndCloseModal () {
+ function handleFormSubmit() {
const file = new ProjectFile(filename, props.projectId, data, Date.now());
- if (type === "openapi"){
- KaravanApi.postOpenApi(file, generateRest, generateRoutes, integrationName, res => {
- if (res.status === 200) {
- EventBus.sendAlert("File uploaded", "", "info")
- closeModal();
- ProjectService.refreshProjectData(props.projectId);
- } else {
- closeModal();
- EventBus.sendAlert("Error", res.statusText, "warning")
- }
- })
+
+ if (type === "openapi") {
+ return ProjectService.createOpenApiFile(file, generateRest, generateRoutes, integrationName)
+ .then(() => handleOnFormSubmitSuccess())
+ .catch((error) => handleOnFormSubmitFailure(error));
} else {
- KaravanApi.postProjectFile(file, res => {
- if (res.status === 200) {
- EventBus.sendAlert("File uploaded", "", "info")
- closeModal();
- ProjectService.refreshProjectData(props.projectId);
- } else {
- closeModal();
- EventBus.sendAlert("Error", res.statusText, "warning")
- }
- })
+ return ProjectService.createFile(file)
+ .then(() => handleOnFormSubmitSuccess())
+ .catch((error) => handleOnFormSubmitFailure(error));
}
}
+ function handleOnFormSubmitSuccess () {
+ const message = "File successfully uploaded.";
+ EventBus.sendAlert( "Success", message, "success");
+
+ closeModal();
+ ProjectService.refreshProjectData(props.projectId);
+ }
+
+ function handleOnFormSubmitFailure(error: AxiosError) {
+ registerResponseErrors(error);
+ }
+
const handleFileInputChange = (file: File) => setFilename(file.name);
const handleFileReadStarted = (fileHandle: File) => setIsLoading(true);
const handleFileReadFinished = (fileHandle: File) => setIsLoading(false);
@@ -90,6 +120,8 @@ export function UploadFileModal(props: Props) {
setFilename('');
setData('');
setIsRejected(false);
+ resetGlobalErrors();
+ reset(defaultFormValues);
};
@@ -104,20 +136,32 @@ export function UploadFileModal(props: Props) {
isOpen={operation === 'upload'}
onClose={closeModal}
actions={[
- <Button key="confirm" variant="primary" onClick={saveAndCloseModal} isDisabled={fileNotUploaded}>Save</Button>,
+ <Button key="confirm" variant="primary" onClick={handleSubmit(handleFormSubmit)} 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' )}
+ onChange={(event, _) => {
+ resetGlobalErrors();
+ clearErrors("upload");
+ setType(_ ? 'integration': 'openapi' );
+ }}
/>{' '}
<Radio value="OpenAPI" label="OpenAPI json/yaml" name="OpenAPI" id="OpenAPI" isChecked={type === 'openapi'}
- onChange={(event, _) => setType( _ ? 'openapi' : 'integration' )}
+ onChange={(event, _) => {
+ resetGlobalErrors();
+ clearErrors("upload");
+ setType( _ ? 'openapi' : 'integration' );
+ }}
/>
<Radio value="Other" label="Other" name="Other" id="Other" isChecked={type === 'other'}
- onChange={(event, _) => setType( _ ? 'other' : 'integration' )}
+ onChange={(event, _) => {
+ resetGlobalErrors();
+ clearErrors("upload");
+ setType( _ ? 'other' : 'integration' );
+ }}
/>
</FormGroup>
<FormGroup fieldId="upload">
@@ -129,16 +173,27 @@ export function UploadFileModal(props: Props) {
hideDefaultPreview
browseButtonText="Upload"
isLoading={isLoading}
- onFileInputChange={(_event, fileHandle: File) => handleFileInputChange(fileHandle)}
- onDataChange={(_event, data) => handleTextOrDataChange(data)}
- onTextChange={(_event, text) => handleTextOrDataChange(text)}
+ onFileInputChange={(_event, fileHandle: File) => {
+ handleFileInputChange(fileHandle);
+ register('upload').onChange(_event);
+ }}
+ onDataChange={(_event, data) => {
+ handleTextOrDataChange(data);
+ register('upload').onChange(_event);
+ }}
+ onTextChange={(_event, text) => {
+ handleTextOrDataChange(data);
+ register('upload').onChange(_event);
+ }}
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'}
+ validated={!!errors.upload && isRejected ? 'error' : 'default'}
+ {...register('upload')}
/>
+ {!!errors.upload && <Text style={{ color: 'red', fontStyle: 'italic'}}>{errors?.upload?.message}</Text>}
</FormGroup>
{type === 'openapi' && <FormGroup fieldId="generateRest">
<Switch
@@ -167,7 +222,14 @@ export function UploadFileModal(props: Props) {
onChange={(_, value) => setIntegrationName(value)}
/>
</FormGroup>}
+ <Grid>
+ {globalErrors &&
+ globalErrors.map((error) => (
+ <Alert title={error} key={error} variant="danger"></Alert>
+ ))}
+ <Divider role="presentation" />
+ </Grid>
</Form>
</Modal>
)
-};
\ No newline at end of file
+}
\ No newline at end of file