Skip to content

Commit

Permalink
feat: ADDON-57046: Added file input component (#187)
Browse files Browse the repository at this point in the history
* feat(CODE): ADDON-57046 Added file input component

* feat(CODE): ADDON-57046 Validation changes

* feat(CODE): ADDON-57046 Addressed review comments
  • Loading branch information
tbalar-splunk authored Dec 9, 2022
1 parent 54cef94 commit e0fb336
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 30 deletions.
78 changes: 78 additions & 0 deletions ui/src/main/webapp/components/FileInputComponent.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React, { Component, useState } from 'react';
import File from "@splunk/react-ui/File";
import styled from 'styled-components';
import PropTypes from 'prop-types';

const FileWrapper = styled(File)`
width: 320px !important;
`;

const supportedFileTypes = ".json";

function FileInputComponent(props) {
const {
field,
disabled,
error,
controlOptions,
handleChange
} = props;

const fileReader = new FileReader();

const [fileName, setFileName] = useState("");

const handleAddFiles = (files) => {
if (files.length) {
const file = files[0];

if (fileReader.readyState === 1) {
fileReader.abort();
};

fileReader.readAsBinaryString(file);

fileReader.onload = () => {
const value = {
fileName: file.name,
fileSize: file.size,
fileContent: fileReader.result
};
setFileName(file.name);
handleChange(field, value);
};
};
};

const handleRemoveFile = () => {
if (fileReader.readyState === 1) {
fileReader.abort();
};
setFileName(null);
handleChange(field, null);
};

return(
<FileWrapper
key={field}
onRequestAdd={handleAddFiles}
onRequestRemove={handleRemoveFile}
supportsMessage={
<> {controlOptions?.fileSupportMessage} </>
}
disabled={disabled}
accept={supportedFileTypes}
>
{fileName && <File.Item error={error} name={fileName} />}
</FileWrapper>
);
}

FileInputComponent.propTypes = {
field: PropTypes.string,
error: PropTypes.bool,
controlOptions: PropTypes.object,
disabled: PropTypes.bool,
};

export default FileInputComponent;
2 changes: 2 additions & 0 deletions ui/src/main/webapp/constants/ControlTypeMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import CheckBoxComponent from '../components/CheckBoxComponent';
import RadioComponent from '../components/RadioComponent';
import PlaceholderComponent from '../components/PlaceholderComponent';
import CustomControl from '../components/CustomControl';
import FileInputComponent from '../components/FileInputComponent';

export default {
text: TextComponent,
Expand All @@ -16,6 +17,7 @@ export default {
multipleSelect: MultiInputComponent,
checkbox: CheckBoxComponent,
radio: RadioComponent,
file: FileInputComponent,
placeholder: PlaceholderComponent,
custom: CustomControl,
};
3 changes: 3 additions & 0 deletions ui/src/main/webapp/constants/constant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
FILE_MAX_SIZE: '512000'
};
3 changes: 3 additions & 0 deletions ui/src/main/webapp/constants/messageDict.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ export default {
21: 'duplicate {{args[0]}} keys is not allowed',
22: 'Field {{args[0]}} must be less than 1024 characters',
23: '"name" feild must be provided for {{args[0]}} \'s entity in configuration file',
24: 'The file must be in {{args[0]}} format',
25: 'The file size should not exceed {{args[0]}}',
26: 'Invalid JSON file',

// general messages, range [100, 499]
100: 'Create New Input',
Expand Down
70 changes: 40 additions & 30 deletions ui/src/main/webapp/schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@
"radio",
"placeholder",
"oauth",
"helpLink"
"helpLink",
"file"
]
},
"help": {
Expand Down Expand Up @@ -244,6 +245,10 @@
"type": "string",
"maxLength": 350
},
"fileSupportMessage": {
"type": "string",
"maxLength": 350
},
"denyList": {
"type": "string",
"maxLength": 350
Expand Down Expand Up @@ -379,6 +384,9 @@
},
{
"$ref": "#/definitions/DateValidator"
},
{
"$ref": "#/definitions/FileValidator"
}
]
}
Expand Down Expand Up @@ -497,10 +505,7 @@
"maxLength": 400
},
"type": {
"type": "string",
"enum": [
"date"
]
"const": "date"
}
},
"required": [
Expand All @@ -516,10 +521,7 @@
"maxLength": 400
},
"type": {
"type": "string",
"enum": [
"email"
]
"const": "email"
}
},
"required": [
Expand Down Expand Up @@ -560,7 +562,8 @@
"radio",
"placeholder",
"oauth",
"helpLink"
"helpLink",
"file"
]
},
"help": {
Expand Down Expand Up @@ -759,6 +762,9 @@
},
{
"$ref": "#/definitions/DateValidator"
},
{
"$ref": "#/definitions/FileValidator"
}
]
}
Expand Down Expand Up @@ -1089,10 +1095,7 @@
"maxLength": 400
},
"type": {
"type": "string",
"enum": [
"ipv4"
]
"const": "ipv4"
}
},
"required": [
Expand Down Expand Up @@ -1143,10 +1146,7 @@
"maxLength": 400
},
"type": {
"type": "string",
"enum": [
"number"
]
"const": "number"
},
"range": {
"type": "array",
Expand All @@ -1161,6 +1161,25 @@
],
"additionalProperties": false
},
"FileValidator": {
"type": "object",
"properties": {
"errorMsg": {
"type": "string",
"maxLength": 400
},
"type": {
"const": "file"
},
"supportedFileTypes": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
},
"OAuthFields": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -1219,10 +1238,7 @@
"maxLength": 400
},
"type": {
"type": "string",
"enum": [
"regex"
]
"const": "regex"
},
"pattern": {
"type": "string"
Expand All @@ -1242,10 +1258,7 @@
"maxLength": 400
},
"type": {
"type": "string",
"enum": [
"string"
]
"const": "string"
},
"minLength": {
"type": "number",
Expand Down Expand Up @@ -1349,10 +1362,7 @@
"maxLength": 400
},
"type": {
"type": "string",
"enum": [
"url"
]
"const": "url"
}
},
"required": [
Expand Down
38 changes: 38 additions & 0 deletions ui/src/main/webapp/util/Validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import {
parseRegexRawStr,
parseStringValidator,
parseFunctionRawStr,
parseFileValidator
} from './uccConfigurationValidators';
import FILE from "../constants/constant";

// Validate provided saveValidator function
export function SaveValidator(validatorFunc, formData) {
Expand Down Expand Up @@ -135,6 +137,32 @@ class Validator {
return false;
}

FileValidator(field, validator, data) {
if(data) {
const { isValidExtension, fileSize, isValidContent } = parseFileValidator(data, validator.supportedFileTypes);
if(!isValidExtension) {
return {
errorField: field,
errorMsg: validator.errorMsg || getFormattedMessage(24, [validator.supportedFileTypes])
};
}
if(fileSize > FILE.FILE_MAX_SIZE) {
const file_size = FILE.FILE_MAX_SIZE/1024 + "KB";
return {
errorField: field,
errorMsg: getFormattedMessage(25, [file_size])
};
}
if (!isValidContent) {
return {
errorField: field,
errorMsg: getFormattedMessage(26)
};
}
}
return false;
}

doValidation(data) {
if (this.isName) {
const targetValue = data.name;
Expand Down Expand Up @@ -271,6 +299,16 @@ class Validator {
return ret;
}
break;
case 'file':
ret = this.FileValidator(
this.entities[i].field,
this.entities[i].validators[j],
data[this.entities[i].field]
);
if (ret) {
return ret;
}
break;
case 'custom':
ret = Validator.CustomValidator(
this.entities[i].validators[j].validatorFunc,
Expand Down
17 changes: 17 additions & 0 deletions ui/src/main/webapp/util/uccConfigurationValidators.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,23 @@ export const parseFunctionRawStr = (rawStr) => {
return { error, result };
};

export const parseFileValidator = (data, validFileTypes) => {

const { fileName, fileSize, fileContent } = data;
const givenFileExtension = fileName.split(".").pop();
const isValidExtension = validFileTypes.findIndex((fileExtension) => fileExtension === givenFileExtension) !== -1;

let isValidContent = true;

try {
JSON.parse(fileContent);
} catch (err) {
isValidContent = false;
}

return { isValidExtension, fileSize, isValidContent };
};

export const checkDupKeyValues = (config, isInput, location) => {
// Forbid dup name/title in services and tabs
const servicesLikeArr = _.get(config, isInput ? 'services' : 'tabs');
Expand Down

0 comments on commit e0fb336

Please sign in to comment.