From b30ea131997278c5d065b61740a367b0a093be56 Mon Sep 17 00:00:00 2001 From: harshpatel Date: Fri, 12 Mar 2021 10:43:59 +0530 Subject: [PATCH 01/12] ADDON-34750: feat: ucc_ui_lib: add dynamic data loading and error boundary --- .../main/webapp/components/ErrorBoundary.jsx | 60 ++ .../webapp/components/table/CustomTable.jsx | 167 +++-- .../webapp/components/table/TableWrapper.jsx | 599 +++--------------- .../src/main/webapp/constants/errorCodes.js | 6 + .../src/main/webapp/pages/Input/InputPage.jsx | 11 +- .../src/main/webapp/pages/entry_page.jsx | 10 +- .../ucc_ui_lib/src/main/webapp/util/util.js | 15 + 7 files changed, 266 insertions(+), 602 deletions(-) create mode 100644 splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ErrorBoundary.jsx create mode 100644 splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/constants/errorCodes.js diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ErrorBoundary.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ErrorBoundary.jsx new file mode 100644 index 000000000..83a99548d --- /dev/null +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ErrorBoundary.jsx @@ -0,0 +1,60 @@ +import React from 'react'; +import Heading from '@splunk/react-ui/Heading'; +import Message from '@splunk/react-ui/Message'; + +import errorCodes from '../constants/errorCodes'; + +class ErrorBoundary extends React.Component { + constructor(props) { + super(props); + this.state = { errorCode: null, error: null, errorInfo: null }; + } + + static getDerivedStateFromError(error) { + // Update state so the next render will show the fallback UI. + return { errorCode: error.ucc_err_code }; + } + + componentDidCatch(error, errorInfo) { + // Catch errors in any components below and re-render with error message + this.setState({ + error: error, + errorInfo: errorInfo, + }); + // You can also log error messages to an error reporting service here + } + + render() { + if (this.state.errorCode) { + // Error path + return ( + <> + + Something went wrong! (ERROR_CODE: {this.state.errorCode}) + + {errorCodes[this.state.errorCode]} +
+ {this.state.error && this.state.error.toString()} +
+ {this.state.errorInfo && this.state.errorInfo.componentStack} +
+ + ); + } else if (this.state.error) { + return ( + <> + Something went wrong! +
+ {this.state.error && this.state.error.toString()} +
+ {this.state.errorInfo && this.state.errorInfo.componentStack} +
+ + ); + } + // Normally, just render children + return this.props.children; + } +} + +export default ErrorBoundary; diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/CustomTable.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/CustomTable.jsx index 9d6bfb54e..d5c43a171 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/CustomTable.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/CustomTable.jsx @@ -8,13 +8,13 @@ import Trash from '@splunk/react-icons/Trash'; import Tooltip from '@splunk/react-ui/Tooltip'; import { _ } from '@splunk/ui-utils/i18n'; import PropTypes from 'prop-types'; +import WaitSpinner from '@splunk/react-ui/WaitSpinner'; import { ActionButtonComponent } from './CustomTableStyle'; import { getUnifiedConfigs } from '../../util/util'; import { getExpansionRow } from './TableExpansionRow'; function CustomTable({ isInput, serviceName, data, handleToggleActionClick }) { - const [sortKey, setSortKey] = useState('name'); const [sortDir, setSortDir] = useState('asc'); @@ -27,14 +27,14 @@ function CustomTable({ isInput, serviceName, data, handleToggleActionClick }) { headers.forEach((header) => { column.push({ ...header, - sortKey: header.field || null + sortKey: header.field || null, }); }); } column.push({ label: 'Actions', field: 'actions', sortKey: '' }); return column; } - } + }; const [columns, setColumns] = useState(() => generateColumns()); @@ -49,57 +49,49 @@ function CustomTable({ isInput, serviceName, data, handleToggleActionClick }) { const getTableHeaders = () => { return ( - {columns && columns.length && columns.map((headData) => ( - - {headData.label} - - ))} + {columns && + columns.length && + columns.map((headData) => ( + + {headData.label} + + ))} - ) - } - - const handleEditActionClick = () => { - - } - - const handleCloneActionClick = () => { + ); + }; - } + const handleEditActionClick = () => {}; - const handleDeleteActionClick = () => { + const handleCloneActionClick = () => {}; - } + const handleDeleteActionClick = () => {}; const rowActionsPrimaryButton = (row) => { return ( - + } onClick={() => handleEditActionClick(row)} /> - + } onClick={() => handleCloneActionClick(row)} /> - + } @@ -108,76 +100,79 @@ function CustomTable({ isInput, serviceName, data, handleToggleActionClick }) { - ) - } + ); + }; const getTableRow = (row) => { return ( - - {columns && columns.length && columns.map((header) => { - if (header.field === "disabled") { - return ( - - handleToggleActionClick(row)} - selected={!row.disabled} - appearance="toggle" - > - {row.disabled ? "Disabled" : "Enabled"} - - - ) - } - - if (header.field === "actions") { - return rowActionsPrimaryButton(row); - } - - return ( - - {row[header.field]} - - ) - - })} + + {columns && + columns.length && + columns.map((header) => { + if (header.field === 'disabled') { + return ( + + handleToggleActionClick(row)} + selected={!row.disabled} + disabled={row.__toggleDisable} + appearance="toggle" + style={{ padding: 0 }} + > + {!row.__toggleDisable ? ( + row.disabled ? ( + 'Disabled' + ) : ( + 'Enabled' + ) + ) : ( + + )} + + + ); + } + + if (header.field === 'actions') { + return rowActionsPrimaryButton(row); + } + + return {row[header.field]}; + })} ); - } + }; const getTableBody = () => { return ( - {data && data.length && data - .sort((rowA, rowB) => { - if (sortDir === 'asc') { - return rowA[sortKey] > rowB[sortKey] ? 1 : -1; - } - if (sortDir === 'desc') { - return rowB[sortKey] > rowA[sortKey] ? 1 : -1; - } - return 0; - }) - .map((row) => ( - getTableRow(row) - )) - } + {data && + data.length && + data + .sort((rowA, rowB) => { + if (sortDir === 'asc') { + return rowA[sortKey] > rowB[sortKey] ? 1 : -1; + } + if (sortDir === 'desc') { + return rowB[sortKey] > rowA[sortKey] ? 1 : -1; + } + return 0; + }) + .map((row) => getTableRow(row))} ); - } + }; return ( <> - { columns && columns.length && + {columns && columns.length && ( {getTableHeaders()} {getTableBody()}
- } + )} ); } @@ -186,7 +181,7 @@ CustomTable.propTypes = { isInput: PropTypes.bool, serviceName: PropTypes.string.isRequired, data: PropTypes.array.isRequired, - handleToggleActionClick: PropTypes.func + handleToggleActionClick: PropTypes.func, }; export default CustomTable; diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx index 3915b3784..839879466 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx @@ -1,6 +1,7 @@ import React, { useState, useContext, useEffect, useCallback, memo } from 'react'; import ColumnLayout from '@splunk/react-ui/ColumnLayout'; import update from 'immutability-helper'; +import axios from 'axios'; import Select from '@splunk/react-ui/Select'; import PropTypes from 'prop-types'; @@ -11,532 +12,64 @@ import { TableSelectBoxWrapper, WaitSpinnerWrapper, } from './CustomTableStyle'; -import { getUnifiedConfigs } from '../../util/util'; +import { getUnifiedConfigs, generateToast } from '../../util/util'; import InputRowContext from '../../context/InputRowContext'; +import { axiosCallWrapper } from '../../util/axiosCallWrapper'; function TableWrapper({ isInput, serviceName }) { const [loading, setLoading] = useState(true); const [searchText, setSearchText] = useState(''); const [searchType, setSearchType] = useState('all'); const [selecetedPage, setSelectedPage] = useState('10'); + const [error, setError] = useState(false); const { rowData, setRowData } = useContext(InputRowContext); + const unifiedConfigs = getUnifiedConfigs(); + useEffect(() => { fetchInputs(); }, [fetchInputs]); const fetchInputs = useCallback(() => { setLoading(true); - setTimeout(() => { - // API call response - const data = [ - [ - { - name: 'account', - id: - 'https://10.202.39.212:8000/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/account', - updated: '1970-01-01T00:00:00+00:00', - links: { - alternate: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/account', - list: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/account', - edit: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/account', - remove: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/account', - }, - author: 'nobody', - acl: { - app: 'Splunk_TA_salesforce', - can_list: true, - can_write: true, - modifiable: false, - owner: 'nobody', - perms: { - read: ['admin', 'power', 'splunk-system-role', 'user'], - write: ['admin', 'splunk-system-role'], - }, - removable: true, - sharing: 'app', - }, - content: { - disabled: false, - 'eai:acl': null, - host: '$decideOnStartup', - host_resolved: 'so1', - index: '11default', - interval: '1200', - limit: '1000', - object: 'Account', - object_fields: 'Id,LastModifiedById,LastModifiedDate,Name', - order_by: 'LastModifiedDate', - 'python.version': null, - sourcetype: 'sfdc:object', - start_by_shell: 'false', - }, - }, - { - name: 'contentversion', - id: - 'https://10.202.39.212:8000/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/contentversion', - updated: '1970-01-01T00:00:00+00:00', - links: { - alternate: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/contentversion', - list: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/contentversion', - edit: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/contentversion', - remove: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/contentversion', - }, - author: 'nobody', - acl: { - app: 'Splunk_TA_salesforce', - can_list: true, - can_write: true, - modifiable: false, - owner: 'nobody', - perms: { - read: ['admin', 'power', 'splunk-system-role', 'user'], - write: ['admin', 'splunk-system-role'], - }, - removable: true, - sharing: 'app', - }, - content: { - disabled: false, - 'eai:acl': null, - host: '$decideOnStartup', - host_resolved: 'so1', - index: 'default', - interval: '1200', - limit: '1000', - object: 'ContentVersion', - object_fields: 'Id,LastModifiedById,LastModifiedDate,Title', - order_by: 'LastModifiedDate', - 'python.version': null, - sourcetype: 'sfdc:object', - start_by_shell: 'false', - }, - }, - { - name: 'dashboard', - id: - 'https://10.202.39.212:8000/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/dashboard', - updated: '1970-01-01T00:00:00+00:00', - links: { - alternate: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/dashboard', - list: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/dashboard', - edit: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/dashboard', - remove: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/dashboard', - }, - author: 'nobody', - acl: { - app: 'Splunk_TA_salesforce', - can_list: true, - can_write: true, - modifiable: false, - owner: 'nobody', - perms: { - read: ['admin', 'power', 'splunk-system-role', 'user'], - write: ['admin', 'splunk-system-role'], - }, - removable: true, - sharing: 'app', - }, - content: { - disabled: true, - 'eai:acl': null, - account: 'Temp1', - host: '$decideOnStartup', - host_resolved: 'so1', - index: '22default', - interval: '1200', - limit: '1000', - object: 'Dashboard', - object_fields: 'Id,LastModifiedDate,Title', - order_by: 'LastModifiedDate', - 'python.version': null, - sourcetype: 'sfdc:object', - start_by_shell: 'false', - }, - }, - { - name: 'loginhistory', - id: - 'https://10.202.39.212:8000/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/loginhistory', - updated: '1970-01-01T00:00:00+00:00', - links: { - alternate: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/loginhistory', - list: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/loginhistory', - edit: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/loginhistory', - remove: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/loginhistory', - }, - author: 'nobody', - acl: { - app: 'Splunk_TA_salesforce', - can_list: true, - can_write: true, - modifiable: false, - owner: 'nobody', - perms: { - read: ['admin', 'power', 'splunk-system-role', 'user'], - write: ['admin', 'splunk-system-role'], - }, - removable: true, - sharing: 'app', - }, - content: { - disabled: true, - 'eai:acl': null, - account: 'Other', - host: '$decideOnStartup', - host_resolved: 'so1', - index: 'default', - interval: '60', - limit: '1000', - object: 'LoginHistory', - object_fields: - 'ApiType,ApiVersion,Application,Browser,ClientVersion,Id,LoginTime,LoginType,LoginUrl,Platform,SourceIp,Status,UserId', - order_by: 'LoginTime', - 'python.version': null, - sourcetype: 'sfdc:object', - start_by_shell: 'false', - }, - }, - { - name: 'opportunity', - id: - 'https://10.202.39.212:8000/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/opportunity', - updated: '1970-01-01T00:00:00+00:00', - links: { - alternate: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/opportunity', - list: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/opportunity', - edit: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/opportunity', - remove: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/opportunity', - }, - author: 'nobody', - acl: { - app: 'Splunk_TA_salesforce', - can_list: true, - can_write: true, - modifiable: false, - owner: 'nobody', - perms: { - read: ['admin', 'power', 'splunk-system-role', 'user'], - write: ['admin', 'splunk-system-role'], - }, - removable: true, - sharing: 'app', - }, - content: { - disabled: true, - 'eai:acl': null, - account: 'Dummy1', - host: '$decideOnStartup', - host_resolved: 'so1', - index: 'default', - interval: '1200', - limit: '1000', - object: 'Opportunity', - object_fields: 'Id,LastModifiedById,LastModifiedDate,Name', - order_by: 'LastModifiedDate', - 'python.version': null, - sourcetype: 'sfdc:object', - start_by_shell: 'false', - }, - }, - { - name: 'report', - id: - 'https://10.202.39.212:8000/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/report', - updated: '1970-01-01T00:00:00+00:00', - links: { - alternate: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/report', - list: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/report', - edit: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/report', - remove: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/report', - }, - author: 'nobody', - acl: { - app: 'Splunk_TA_salesforce', - can_list: true, - can_write: true, - modifiable: false, - owner: 'nobody', - perms: { - read: ['admin', 'power', 'splunk-system-role', 'user'], - write: ['admin', 'splunk-system-role'], - }, - removable: true, - sharing: 'app', - }, - content: { - disabled: true, - 'eai:acl': null, - account: 'Test', - host: '$decideOnStartup', - host_resolved: 'so1', - index: 'default', - interval: '1200', - limit: '1000', - object: 'Report', - object_fields: 'Id,LastModifiedDate,Name', - order_by: 'LastModifiedDate', - 'python.version': null, - sourcetype: 'sfdc:object', - start_by_shell: 'false', - }, - }, - { - name: 'user', - id: - 'https://10.202.39.212:8000/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/user', - updated: '1970-01-01T00:00:00+00:00', - links: { - alternate: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/user', - list: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/user', - edit: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/user', - remove: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_object/user', - }, - author: 'nobody', - acl: { - app: 'Splunk_TA_salesforce', - can_list: true, - can_write: true, - modifiable: false, - owner: 'nobody', - perms: { - read: ['admin', 'power', 'splunk-system-role', 'user'], - write: ['admin', 'splunk-system-role'], - }, - removable: true, - sharing: 'app', - }, - content: { - disabled: true, - account: 'Tushar', - 'eai:acl': null, - host: '$decideOnStartup', - host_resolved: 'so1', - index: 'default', - interval: '1200', - limit: '1000', - object: 'User', - object_fields: - 'LastModifiedDate,City,Country,FirstName,Id,IsActive,LastLoginDate,LastName,Latitude,Longitude,MobilePhone,Name,PostalCode,State,Username,UserRoleId,UserType,Email,CompanyName,ProfileId,Profile.PermissionsApiEnabled,Profile.PermissionsModifyAllData,Profile.PermissionsViewSetup', - order_by: 'LastModifiedDate', - 'python.version': null, - sourcetype: 'sfdc:object', - start_by_shell: 'false', - }, - }, - ], - [ - { - name: 'klrhbfka', - id: - 'https://10.202.23.134:8000/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/klrhbfka', - updated: '1970-01-01T00:00:00+00:00', - links: { - alternate: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/klrhbfka', - list: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/klrhbfka', - edit: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/klrhbfka', - remove: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/klrhbfka', - }, - author: 'nobody', - acl: { - app: 'Splunk_TA_salesforce', - can_list: true, - can_write: true, - modifiable: false, - owner: 'nobody', - perms: { - read: ['admin', 'power', 'splunk-system-role', 'user'], - write: ['admin', 'splunk-system-role'], - }, - removable: true, - sharing: 'app', - }, - content: { - account: 'dishank', - disabled: true, - 'eai:acl': null, - host: '$decideOnStartup', - host_resolved: 'so1', - index: 'default', - interval: '36000', - monitoring_interval: 'Daily', - 'python.version': null, - sourcetype: 'sfdc:logfile', - start_by_shell: 'false', - }, - }, - { - name: 'test', - id: - 'https://10.202.23.134:8000/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/test', - updated: '1970-01-01T00:00:00+00:00', - links: { - alternate: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/test', - list: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/test', - edit: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/test', - remove: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/test', - }, - author: 'nobody', - acl: { - app: 'Splunk_TA_salesforce', - can_list: true, - can_write: true, - modifiable: false, - owner: 'nobody', - perms: { - read: ['admin', 'power', 'splunk-system-role', 'user'], - write: ['admin', 'splunk-system-role'], - }, - removable: true, - sharing: 'app', - }, - content: { - account: 'dishank', - disabled: true, - 'eai:acl': null, - host: '$decideOnStartup', - host_resolved: 'so1', - index: 'default', - interval: '36000', - monitoring_interval: 'Daily', - 'python.version': null, - sourcetype: 'sfdc:logfile', - start_by_shell: 'false', - }, - }, - { - name: 'test123', - id: - 'https://10.202.23.134:8000/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/test123', - updated: '1970-01-01T00:00:00+00:00', - links: { - alternate: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/test123', - list: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/test123', - edit: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/test123', - remove: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/test123', - }, - author: 'nobody', - acl: { - app: 'Splunk_TA_salesforce', - can_list: true, - can_write: true, - modifiable: false, - owner: 'nobody', - perms: { - read: ['admin', 'power', 'splunk-system-role', 'user'], - write: ['admin', 'splunk-system-role'], - }, - removable: true, - sharing: 'app', - }, - content: { - account: 'dishank', - disabled: true, - 'eai:acl': null, - host: '$decideOnStartup', - host_resolved: 'so1', - index: 'default', - interval: '30000', - monitoring_interval: 'Daily', - 'python.version': null, - sourcetype: 'sfdc:logfile', - start_by_shell: 'false', - }, - }, - { - name: 'test_hook', - id: - 'https://10.202.23.134:8000/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/test_hook', - updated: '1970-01-01T00:00:00+00:00', - links: { - alternate: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/test_hook', - list: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/test_hook', - edit: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/test_hook', - remove: - '/servicesNS/nobody/Splunk_TA_salesforce/Splunk_TA_salesforce_sfdc_event_log/test_hook', - }, - author: 'nobody', - acl: { - app: 'Splunk_TA_salesforce', - can_list: true, - can_write: true, - modifiable: false, - owner: 'nobody', - perms: { - read: ['admin', 'power', 'splunk-system-role', 'user'], - write: ['admin', 'splunk-system-role'], - }, - removable: true, - sharing: 'app', - }, - content: { - account: 'dishank', - disabled: false, - 'eai:acl': null, - host: '$decideOnStartup', - host_resolved: 'so1', - index: 'default', - interval: '360000', - monitoring_interval: 'Daily', - 'python.version': null, - sourcetype: 'sfdc:logfile', - start_by_shell: 'false', - }, - }, - ], - ]; - modifyAPIResponse(data); - }, 1000); + let requests = []; + unifiedConfigs.pages.inputs.services.forEach((service) => { + requests.push(axiosCallWrapper(service.name)); + }); + axios + .all(requests) + .catch((error) => { + let message = ''; + if (error.response) { + // The request was made and the server responded with a status code + message = `Error received from server: ${error.response.data.messages[0].text}`; + error.ucc_err_code = 'ERR0001'; + generateToast(message); + } else if (error.request) { + console.log(error.request); + // The request was made but no response was received + message = `No response received while making request to input services`; + error.ucc_err_code = 'ERR0002'; + generateToast(message); + } else { + // Something happened in setting up the request that triggered an Error + message = `Error making request to input services`; + error.ucc_err_code = 'ERR0003'; + generateToast(message); + } + setError(error); + setLoading(false); + return Promise.reject(error); + }) + .then((response) => { + // const isNotUndefined = response.every(Boolean); + modifyAPIResponse(response.map((res) => res.data.entry)); + }); }, [modifyAPIResponse]); const modifyAPIResponse = useCallback( (data) => { - const unifiedConfigs = getUnifiedConfigs(); const obj = {}; unifiedConfigs.pages.inputs.services.forEach((service, index) => { if (service && service.name && data) { @@ -564,9 +97,55 @@ function TableWrapper({ isInput, serviceName }) { */ const changeStatus = (row) => { const updatedRowData = update(rowData, { - [row.serviceName]: { [row.name]: { disabled: { $set: !row.disabled } } }, + [row.serviceName]: { + [row.name]: { __toggleDisable: { $set: true } }, + }, }); setRowData(updatedRowData); + const params = new URLSearchParams(); + params.append('disabled', !row.disabled); + axiosCallWrapper( + `${row.serviceName}/${row.name}`, + null, + params, + { 'Content-Type': 'application/x-www-form-urlencoded' }, + 'post' + ) + .catch((error) => { + let message = ''; + if (error.response) { + // The request was made and the server responded with a status code + message = `Error received from server: ${error.response.data.messages[0].text}`; + generateToast(message); + } else if (error.request) { + console.log(error.request); + // The request was made but no response was received + message = `No response received while making request to ${row.serviceName}/${row.name}`; + generateToast(message); + } else { + // Something happened in setting up the request that triggered an Error + message = `Error making request to ${row.serviceName}/${row.name}`; + generateToast(message); + } + const updatedRowData = update(rowData, { + [row.serviceName]: { + [row.name]: { __toggleDisable: { $set: false } }, + }, + }); + setRowData(updatedRowData); + return Promise.reject(error); + }) + .then((response) => { + const updatedRowData = update(rowData, { + [row.serviceName]: { + [row.name]: { + disabled: { $set: response.data.entry[0].content.disabled }, + __toggleDisable: { $set: false }, + }, + }, + }); + setRowData(updatedRowData); + }); }; const handleFilterChange = (e, { value }) => { @@ -629,11 +208,15 @@ function TableWrapper({ isInput, serviceName }) { }); return arr; } - return findByMatchingValue(rowData[searchType]); + return findByMatchingValue(rowData[searchType] || []); }; const filteredData = !loading && getRowData(); + if (error.ucc_err_code) { + throw error; + } + return ( <> {loading ? ( diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/constants/errorCodes.js b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/constants/errorCodes.js new file mode 100644 index 000000000..c8ceebfc9 --- /dev/null +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/constants/errorCodes.js @@ -0,0 +1,6 @@ +export default { + ERR0001: + 'Failed to load inputs due to error status code! This error can be ignored if you are seeing this error on SplunkCloud search-head.', + ERR0002: 'Failed to load inputs due to no response from server!', + ERR0003: 'Failed to load inputs due to failed request processing!', +}; diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/Input/InputPage.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/Input/InputPage.jsx index b8f372a82..ead87d247 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/Input/InputPage.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/Input/InputPage.jsx @@ -1,12 +1,13 @@ import React, { useState, useEffect } from 'react'; import { _ } from '@splunk/ui-utils/i18n'; +import ToastMessages from '@splunk/react-toast-notifications/ToastMessages'; import { getUnifiedConfigs } from '../../util/util'; import { TitleComponent, SubTitleComponent } from './InputPageStyle'; import TableWrapper from '../../components/table/TableWrapper'; +import ErrorBoundary from '../../components/ErrorBoundary'; function InputPage({ isInput, serviceName }) { - const [title, setTitle] = useState(null); const [description, setDescription] = useState(null); @@ -20,10 +21,10 @@ function InputPage({ isInput, serviceName }) { <> {title} {description} - + + + + ); } diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/entry_page.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/entry_page.jsx index fbb0223e3..384aee8f1 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/entry_page.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/entry_page.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Suspense } from 'react'; import layout from '@splunk/react-page'; import { SplunkThemeProvider } from '@splunk/themes'; import { defaultTheme } from '@splunk/splunk-utils/themes'; @@ -7,6 +7,10 @@ import { StyledContainer } from './EntryPageStyle'; import ConfigManager from '../util/configManager'; import InputPage from './Input/InputPage'; import ConfigurationPage from './Configuration/ConfigurationPage'; +import ErrorBoundary from '../components/ErrorBoundary'; + +// const InputPage = React.lazy(() => import('./Input/InputPage')); +// const ConfigurationPage = React.lazy(() => import('./Configuration/ConfigurationPage')); import { InputRowContextProvider } from '../context/InputRowContext'; const defaultThemeSplunkThemeProviderMap = { @@ -42,7 +46,7 @@ if (page === 'inputs') { {({ loading, appData }) => { - return !loading && appData && + return !loading && appData && ; }} @@ -56,7 +60,7 @@ if (page === 'inputs') { {({ loading, appData }) => { - return !loading && appData && + return !loading && appData && ; }} diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/util/util.js b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/util/util.js index 24e75e3a0..83a80a3ac 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/util/util.js +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/util/util.js @@ -1,3 +1,6 @@ +import { TOAST_TYPES } from '@splunk/react-toast-notifications/ToastConstants'; +import Toaster, { makeCreateToast } from '@splunk/react-toast-notifications/Toaster'; + let appData = null; let unifiedConfigs = null; @@ -22,3 +25,15 @@ export function setUnifiedConfig(unifiedConfig) { export function getUnifiedConfigs() { return unifiedConfigs; } + +const createToast = makeCreateToast(Toaster); +export const generateToast = (message, action = undefined) => { + createToast({ + type: TOAST_TYPES.ERROR, + message, + autoDismiss: true, + dismissOnActionClick: true, + showAction: Boolean(action), + action: action ? action : undefined, + }); +}; From 88a1ce90e53c9e458b378a331256c44113b4a7da Mon Sep 17 00:00:00 2001 From: harshpatel Date: Fri, 12 Mar 2021 11:02:31 +0530 Subject: [PATCH 02/12] fix: add missing react-toast-notifications dep --- .../ucc_ui_lib/package.json | 1 + .../ucc_ui_lib/yarn.lock | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/package.json b/splunk_add_on_ucc_framework/ucc_ui_lib/package.json index a5ead9af2..ca79fef18 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/package.json +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/package.json @@ -26,6 +26,7 @@ "@splunk/babel-preset": "^3.0.0", "@splunk/eslint-config": "^4.0.0", "@splunk/react-page": "^5.0.0", + "@splunk/react-toast-notifications": "^0.9.0", "@splunk/react-ui": "^4.0.0", "@splunk/splunk-utils": "^2.0.0", "@splunk/stylelint-config": "^4.0.0", diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/yarn.lock b/splunk_add_on_ucc_framework/ucc_ui_lib/yarn.lock index f63339fc0..4a2f5caae 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/yarn.lock +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/yarn.lock @@ -1236,6 +1236,19 @@ prop-types "^15.6.2" scriptjs "^2.5.8" +"@splunk/react-toast-notifications@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@splunk/react-toast-notifications/-/react-toast-notifications-0.9.0.tgz#0283ae0f981efebb0c804ac11a9e2718473d1986" + integrity sha512-r+7Zl+zjo+X1G9kPj0wyq8CLyw8FKUv4Rdlpzx+qVC8jwUELZFCnDJNs/lhtE7ColRsc8/jxlKx3J1f9I4mX8Q== + dependencies: + "@splunk/react-icons" "^3.0.1" + "@splunk/react-ui" "^4.0.0" + "@splunk/themes" "^0.7.0" + "@splunk/ui-utils" "^1.2.1" + lodash "^4.17.14" + prop-types "^15.6.2" + react-flip-move "^3.0.1" + "@splunk/react-ui@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@splunk/react-ui/-/react-ui-4.0.0.tgz#41f51f891f3c345857477b25fb2eb56ad4d4e2f5" @@ -7217,6 +7230,11 @@ react-event-listener@^0.6.2: prop-types "^15.6.0" warning "^4.0.1" +react-flip-move@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-flip-move/-/react-flip-move-3.0.4.tgz#261f66101fbc305f9b7b28959c5cf8236413ca74" + integrity sha512-HyUVv9g3t/BS7Yz9HgrtYSWyRNdR2F81nkj+C5iRY675AwlqCLB5JU9mnZWg0cdVz7IM4iquoyZx70vzZv3Z8Q== + react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" From 4e6304176adbace7c22c3fde15f44d45850fbc4c Mon Sep 17 00:00:00 2001 From: harshpatel Date: Fri, 12 Mar 2021 20:59:46 +0530 Subject: [PATCH 03/12] fix: ucc_ui_lib: asynchronous state updates for status toggle button --- .../webapp/components/table/TableWrapper.jsx | 473 ++++++++++-------- .../main/webapp/context/InputRowContext.jsx | 37 +- 2 files changed, 280 insertions(+), 230 deletions(-) diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx index 839879466..0c77afbb9 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx @@ -1,4 +1,4 @@ -import React, { useState, useContext, useEffect, useCallback, memo } from 'react'; +import React from 'react'; import ColumnLayout from '@splunk/react-ui/ColumnLayout'; import update from 'immutability-helper'; import axios from 'axios'; @@ -16,25 +16,31 @@ import { getUnifiedConfigs, generateToast } from '../../util/util'; import InputRowContext from '../../context/InputRowContext'; import { axiosCallWrapper } from '../../util/axiosCallWrapper'; -function TableWrapper({ isInput, serviceName }) { - const [loading, setLoading] = useState(true); - const [searchText, setSearchText] = useState(''); - const [searchType, setSearchType] = useState('all'); - const [selecetedPage, setSelectedPage] = useState('10'); - const [error, setError] = useState(false); +class TableWrapper extends React.PureComponent { + static contextType = InputRowContext; - const { rowData, setRowData } = useContext(InputRowContext); - - const unifiedConfigs = getUnifiedConfigs(); + constructor() { + super(); + this.state = { + loading: true, + searchText: '', + searchType: 'all', + selectedPage: '10', + error: false, + }; + this.unifiedConfigs = getUnifiedConfigs(); + } - useEffect(() => { - fetchInputs(); - }, [fetchInputs]); + componentDidMount() { + this.fetchInputs(); + } - const fetchInputs = useCallback(() => { - setLoading(true); - let requests = []; - unifiedConfigs.pages.inputs.services.forEach((service) => { + fetchInputs() { + this.setState({ + loading: true, + }); + const requests = []; + this.unifiedConfigs.pages.inputs.services.forEach((service) => { requests.push(axiosCallWrapper(service.name)); }); axios @@ -47,7 +53,6 @@ function TableWrapper({ isInput, serviceName }) { error.ucc_err_code = 'ERR0001'; generateToast(message); } else if (error.request) { - console.log(error.request); // The request was made but no response was received message = `No response received while making request to input services`; error.ucc_err_code = 'ERR0002'; @@ -58,229 +63,257 @@ function TableWrapper({ isInput, serviceName }) { error.ucc_err_code = 'ERR0003'; generateToast(message); } - setError(error); - setLoading(false); + this.setState({ + error, + }); + this.setState({ + loading: false, + }); return Promise.reject(error); }) .then((response) => { // const isNotUndefined = response.every(Boolean); - modifyAPIResponse(response.map((res) => res.data.entry)); - }); - }, [modifyAPIResponse]); - - const modifyAPIResponse = useCallback( - (data) => { - const obj = {}; - unifiedConfigs.pages.inputs.services.forEach((service, index) => { - if (service && service.name && data) { - const tmpObj = {}; - data[index].forEach((val) => { - tmpObj[val.name] = { - ...val.content, - id: val.id, - name: val.name, - serviceName: service.name, - }; - }); - obj[service.name] = tmpObj; - } + this.modifyAPIResponse(response.map((res) => res.data.entry)); }); - setRowData(obj); - setLoading(false); - }, - [setRowData] - ); + } - /** - * - * @param row {Object} row - */ - const changeStatus = (row) => { - const updatedRowData = update(rowData, { - [row.serviceName]: { - [row.name]: { __toggleDisable: { $set: true } }, - }, - }); - setRowData(updatedRowData); - const params = new URLSearchParams(); - params.append('disabled', !row.disabled); - axiosCallWrapper( - `${row.serviceName}/${row.name}`, - null, - params, - { 'Content-Type': 'application/x-www-form-urlencoded' }, - 'post' - ) - .catch((error) => { - let message = ''; - if (error.response) { - // The request was made and the server responded with a status code - message = `Error received from server: ${error.response.data.messages[0].text}`; - generateToast(message); - } else if (error.request) { - console.log(error.request); - // The request was made but no response was received - message = `No response received while making request to ${row.serviceName}/${row.name}`; - generateToast(message); - } else { - // Something happened in setting up the request that triggered an Error - message = `Error making request to ${row.serviceName}/${row.name}`; - generateToast(message); - } - const updatedRowData = update(rowData, { - [row.serviceName]: { - [row.name]: { __toggleDisable: { $set: false } }, - }, + modifyAPIResponse(data) { + const obj = {}; + this.unifiedConfigs.pages.inputs.services.forEach((service, index) => { + if (service && service.name && data) { + const tmpObj = {}; + data[index].forEach((val) => { + tmpObj[val.name] = { + ...val.content, + id: val.id, + name: val.name, + serviceName: service.name, + }; }); - setRowData(updatedRowData); - return Promise.reject(error); - }) - .then((response) => { - const updatedRowData = update(rowData, { - [row.serviceName]: { - [row.name]: { - disabled: { $set: response.data.entry[0].content.disabled }, - __toggleDisable: { $set: false }, + obj[service.name] = tmpObj; + } + }); + this.context.setRowData({ rowData: obj }); + this.setState({ + loading: false, + }); + } + + render() { + const { rowData, setRowData } = this.context; + const { isInput, serviceName } = this.props; + + /** + * + * @param row {Object} row + */ + const changeStatus = (row) => { + setRowData((state) => { + return { + rowData: update(state.rowData, { + [row.serviceName]: { + [row.name]: { __toggleDisable: { $set: true } }, }, - }, - }); - setRowData(updatedRowData); + }), + }; }); - }; + const params = new URLSearchParams(); + params.append('disabled', !row.disabled); + axiosCallWrapper( + `${row.serviceName}/${row.name}`, + null, + params, + { 'Content-Type': 'application/x-www-form-urlencoded' }, + 'post' + ) + .catch((error) => { + let message = ''; + if (error.response) { + // The request was made and the server responded with a status code + message = `Error received from server: ${error.response.data.messages[0].text}`; + generateToast(message); + } else if (error.request) { + // The request was made but no response was received + message = `No response received while making request to ${row.serviceName}/${row.name}`; + generateToast(message); + } else { + // Something happened in setting up the request that triggered an Error + message = `Error making request to ${row.serviceName}/${row.name}`; + generateToast(message); + } + setRowData((state) => { + return { + rowData: update(state.rowData, { + [row.serviceName]: { + [row.name]: { __toggleDisable: { $set: false } }, + }, + }), + }; + }); + return Promise.reject(error); + }) + .then((response) => { + setRowData((state) => { + return { + rowData: update(state.rowData, { + [row.serviceName]: { + [row.name]: { + disabled: { $set: response.data.entry[0].content.disabled }, + __toggleDisable: { $set: false }, + }, + }, + }), + }; + }); + }); + }; - const handleFilterChange = (e, { value }) => { - setSearchText(value); - }; + const handleFilterChange = (e, { value }) => { + this.setState({ + searchText: value, + }); + }; - const handleChange = (e, { value }) => { - setSearchType(value); - }; + const handleChange = (e, { value }) => { + this.setState({ + searchType: value, + }); + }; - const getSearchTypeDropdown = () => { - const unifiedConfigs = getUnifiedConfigs(); - const { services } = unifiedConfigs.pages.inputs; + const getSearchTypeDropdown = () => { + const { services } = this.unifiedConfigs.pages.inputs; - let arr = []; - arr = services.map((service) => { - return ; - }); + let arr = []; + arr = services.map((service) => { + return ( + + ); + }); - arr.unshift(); - return arr; - }; + arr.unshift(); + return arr; + }; - /** - * - * @param {Array} data - * This function will iterate an arrray and match each key-value with the searchText - * It will return a new array which will match with searchText - */ - const findByMatchingValue = (data) => { - const arr = []; - Object.keys(data).forEach((v) => { - let found = false; - Object.keys(data[v]).forEach((vv) => { - if ( - typeof data[v][vv] === 'string' && - data[v][vv].toLowerCase().includes(searchText.toLowerCase()) - ) { - if (!found) { - arr.push(data[v]); - found = true; + /** + * + * @param {Array} data + * This function will iterate an arrray and match each key-value with the searchText + * It will return a new array which will match with searchText + */ + const findByMatchingValue = (data) => { + const arr = []; + Object.keys(data).forEach((v) => { + let found = false; + Object.keys(data[v]).forEach((vv) => { + if ( + typeof data[v][vv] === 'string' && + data[v][vv].toLowerCase().includes(this.state.searchText.toLowerCase()) + ) { + if (!found) { + arr.push(data[v]); + found = true; + } } - } - }); - }); - return arr; - }; - - const getRowData = () => { - if (searchType === 'all') { - let arr = []; - Object.keys(rowData).forEach((key) => { - let newArr = []; - if (searchText && searchText.length) { - newArr = findByMatchingValue(rowData[key]); - } else { - newArr = Object.keys(rowData[key]).map((val) => rowData[key][val]); - } - arr = arr.concat(newArr); + }); }); return arr; - } - return findByMatchingValue(rowData[searchType] || []); - }; + }; - const filteredData = !loading && getRowData(); + const getRowData = () => { + if (this.state.searchType === 'all') { + let arr = []; + Object.keys(rowData).forEach((key) => { + let newArr = []; + if (this.state.searchText && this.state.searchText.length) { + newArr = findByMatchingValue(rowData[key]); + } else { + newArr = Object.keys(rowData[key]).map((val) => rowData[key][val]); + } + arr = arr.concat(newArr); + }); + return arr; + } + return findByMatchingValue(rowData[this.state.searchType] || []); + }; - if (error.ucc_err_code) { - throw error; - } + const filteredData = !this.state.loading && getRowData(); - return ( - <> - {loading ? ( - - ) : ( - <> - - - - -
- {filteredData.length} Input - {filteredData.length > 1 && s} - - - - -
-
-
- - - - -
-
+ if (this.state.error.ucc_err_code) { + throw this.state.error; + } + + return ( + <> + {this.state.loading ? ( + + ) : ( + <> + + + + +
+ {filteredData.length} Input + {filteredData.length > 1 && s} + + + + +
+
+
+ + + + +
+
- changeStatus(row)} - /> - - )} - - ); + changeStatus(row)} + /> + + )} + + ); + } } TableWrapper.propTypes = { @@ -288,4 +321,4 @@ TableWrapper.propTypes = { serviceName: PropTypes.string.isRequired, }; -export default memo(TableWrapper); +export default TableWrapper; diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/context/InputRowContext.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/context/InputRowContext.jsx index 9438fb486..38b6a9d6c 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/context/InputRowContext.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/context/InputRowContext.jsx @@ -1,19 +1,36 @@ -import React, { createContext, useState } from "react"; +import React, { createContext } from 'react'; +import PropTypes from 'prop-types'; const InputRowContext = createContext({ rowData: {}, - setRowData: () => { }, + setRowData: () => {}, }); -export const InputRowContextProvider = ({ children }) => { +export class InputRowContextProvider extends React.PureComponent { + constructor() { + super(); + this.state = { + rowData: {}, + }; + this.setState = this.setState.bind(this); + } - const [rowData, setRowData] = useState({}); - - return ( - - {children} - - ); + render() { + return ( + + {this.props.children} + + ); + } } +InputRowContextProvider.propTypes = { + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node + ]).isRequired +}; + export default InputRowContext; From 3e2d15dffdfe01d5f4d5315ef7aedb52aef57f79 Mon Sep 17 00:00:00 2001 From: harshpatel Date: Mon, 15 Mar 2021 19:30:39 +0530 Subject: [PATCH 04/12] fix: error handling and migrated to functional component --- .../main/webapp/components/ErrorBoundary.jsx | 18 +- .../webapp/components/table/CustomTable.jsx | 19 +- .../webapp/components/table/TableWrapper.jsx | 460 ++++++++---------- .../main/webapp/context/InputRowContext.jsx | 33 +- .../src/main/webapp/errors/errorWithCode.js | 7 + .../src/main/webapp/pages/entry_page.jsx | 5 +- .../src/main/webapp/util/axiosCallWrapper.js | 62 ++- 7 files changed, 299 insertions(+), 305 deletions(-) create mode 100644 splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/errors/errorWithCode.js diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ErrorBoundary.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ErrorBoundary.jsx index 83a99548d..0496fe746 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ErrorBoundary.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ErrorBoundary.jsx @@ -1,8 +1,10 @@ import React from 'react'; import Heading from '@splunk/react-ui/Heading'; import Message from '@splunk/react-ui/Message'; +import PropTypes from 'prop-types'; import errorCodes from '../constants/errorCodes'; +import ErrorWithCode from '../errors/errorWithCode'; class ErrorBoundary extends React.Component { constructor(props) { @@ -12,14 +14,16 @@ class ErrorBoundary extends React.Component { static getDerivedStateFromError(error) { // Update state so the next render will show the fallback UI. - return { errorCode: error.ucc_err_code }; + if (error instanceof ErrorWithCode) { + return { errorCode: error.errorCode }; + } } componentDidCatch(error, errorInfo) { // Catch errors in any components below and re-render with error message this.setState({ - error: error, - errorInfo: errorInfo, + error, + errorInfo, }); // You can also log error messages to an error reporting service here } @@ -40,7 +44,9 @@ class ErrorBoundary extends React.Component { ); - } else if (this.state.error) { + } + + if (this.state.error) { return ( <> Something went wrong! @@ -57,4 +63,8 @@ class ErrorBoundary extends React.Component { } } +ErrorBoundary.propTypes = { + children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired, +}; + export default ErrorBoundary; diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/CustomTable.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/CustomTable.jsx index d5c43a171..b678f8842 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/CustomTable.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/CustomTable.jsx @@ -104,6 +104,13 @@ function CustomTable({ isInput, serviceName, data, handleToggleActionClick }) { }; const getTableRow = (row) => { + let statusContent = ''; + // eslint-disable-next-line no-underscore-dangle + if (!row.__toggleDisable) { + statusContent = row.disabled ? 'Disabled' : 'Enabled'; + } else { + statusContent = ; + } return ( {columns && @@ -120,16 +127,10 @@ function CustomTable({ isInput, serviceName, data, handleToggleActionClick }) { disabled={row.__toggleDisable} appearance="toggle" style={{ padding: 0 }} + selectedLabel="Enabled" + unselectedLabel="Disabled" > - {!row.__toggleDisable ? ( - row.disabled ? ( - 'Disabled' - ) : ( - 'Enabled' - ) - ) : ( - - )} + {statusContent} ); diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx index 0c77afbb9..97b1500b4 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState, useContext, useEffect, memo } from 'react'; import ColumnLayout from '@splunk/react-ui/ColumnLayout'; import update from 'immutability-helper'; import axios from 'axios'; @@ -15,305 +15,261 @@ import { import { getUnifiedConfigs, generateToast } from '../../util/util'; import InputRowContext from '../../context/InputRowContext'; import { axiosCallWrapper } from '../../util/axiosCallWrapper'; +import { ErrorWithCode } from '../../errors/errorWithCode'; -class TableWrapper extends React.PureComponent { - static contextType = InputRowContext; +function TableWrapper({ isInput, serviceName }) { + const [loading, setLoading] = useState(true); + const [searchText, setSearchText] = useState(''); + const [searchType, setSearchType] = useState('all'); + const [selecetedPage, setSelectedPage] = useState('10'); + const [error, setError] = useState(null); + const [errorCode, setErrorCode] = useState(''); - constructor() { - super(); - this.state = { - loading: true, - searchText: '', - searchType: 'all', - selectedPage: '10', - error: false, - }; - this.unifiedConfigs = getUnifiedConfigs(); - } + const { rowData, setRowData } = useContext(InputRowContext); - componentDidMount() { - this.fetchInputs(); - } + const unifiedConfigs = getUnifiedConfigs(); - fetchInputs() { - this.setState({ - loading: true, + const modifyAPIResponse = (data) => { + const obj = {}; + unifiedConfigs.pages.inputs.services.forEach((service, index) => { + if (service && service.name && data) { + const tmpObj = {}; + data[index].forEach((val) => { + tmpObj[val.name] = { + ...val.content, + id: val.id, + name: val.name, + serviceName: service.name, + }; + }); + obj[service.name] = tmpObj; + } }); + setRowData(obj); + setLoading(false); + }; + + const fetchInputs = () => { + setLoading(true); const requests = []; - this.unifiedConfigs.pages.inputs.services.forEach((service) => { - requests.push(axiosCallWrapper(service.name)); + unifiedConfigs.pages.inputs.services.forEach((service) => { + requests.push( + axiosCallWrapper({ + serviceName: service.name, + }) + ); }); axios .all(requests) + // eslint-disable-next-line no-shadow .catch((error) => { let message = ''; if (error.response) { // The request was made and the server responded with a status code message = `Error received from server: ${error.response.data.messages[0].text}`; - error.ucc_err_code = 'ERR0001'; + setErrorCode('ERR0001'); generateToast(message); } else if (error.request) { // The request was made but no response was received message = `No response received while making request to input services`; - error.ucc_err_code = 'ERR0002'; + setErrorCode('ERR0002'); generateToast(message); } else { // Something happened in setting up the request that triggered an Error message = `Error making request to input services`; - error.ucc_err_code = 'ERR0003'; + setErrorCode('ERR0003'); generateToast(message); } - this.setState({ - error, - }); - this.setState({ - loading: false, - }); + setError(error); + setLoading(false); return Promise.reject(error); }) .then((response) => { // const isNotUndefined = response.every(Boolean); - this.modifyAPIResponse(response.map((res) => res.data.entry)); + modifyAPIResponse(response.map((res) => res.data.entry)); }); - } + }; - modifyAPIResponse(data) { - const obj = {}; - this.unifiedConfigs.pages.inputs.services.forEach((service, index) => { - if (service && service.name && data) { - const tmpObj = {}; - data[index].forEach((val) => { - tmpObj[val.name] = { - ...val.content, - id: val.id, - name: val.name, - serviceName: service.name, - }; - }); - obj[service.name] = tmpObj; - } - }); - this.context.setRowData({ rowData: obj }); - this.setState({ - loading: false, - }); - } + useEffect(() => { + fetchInputs(); + }, []); - render() { - const { rowData, setRowData } = this.context; - const { isInput, serviceName } = this.props; + /** + * + * @param row {Object} row + */ + const changeStatus = (row) => { + setRowData((currentRowData) => { + return update(currentRowData, { + [row.serviceName]: { + [row.name]: { __toggleDisable: { $set: true } }, + }, + }); + }); + const params = new URLSearchParams(); + params.append('disabled', !row.disabled); - /** - * - * @param row {Object} row - */ - const changeStatus = (row) => { - setRowData((state) => { - return { - rowData: update(state.rowData, { + axiosCallWrapper({ + serviceName: `${row.serviceName}/${row.name}`, + params, + customHeaders: { 'Content-Type': 'application/x-www-form-urlencoded' }, + method: 'post', + handleError: true, + callbackOnError: () => { + setRowData((currentRowData) => { + return update(currentRowData, { [row.serviceName]: { - [row.name]: { __toggleDisable: { $set: true } }, + [row.name]: { __toggleDisable: { $set: false } }, }, - }), - }; - }); - const params = new URLSearchParams(); - params.append('disabled', !row.disabled); - axiosCallWrapper( - `${row.serviceName}/${row.name}`, - null, - params, - { 'Content-Type': 'application/x-www-form-urlencoded' }, - 'post' - ) - .catch((error) => { - let message = ''; - if (error.response) { - // The request was made and the server responded with a status code - message = `Error received from server: ${error.response.data.messages[0].text}`; - generateToast(message); - } else if (error.request) { - // The request was made but no response was received - message = `No response received while making request to ${row.serviceName}/${row.name}`; - generateToast(message); - } else { - // Something happened in setting up the request that triggered an Error - message = `Error making request to ${row.serviceName}/${row.name}`; - generateToast(message); - } - setRowData((state) => { - return { - rowData: update(state.rowData, { - [row.serviceName]: { - [row.name]: { __toggleDisable: { $set: false } }, - }, - }), - }; - }); - return Promise.reject(error); - }) - .then((response) => { - setRowData((state) => { - return { - rowData: update(state.rowData, { - [row.serviceName]: { - [row.name]: { - disabled: { $set: response.data.entry[0].content.disabled }, - __toggleDisable: { $set: false }, - }, - }, - }), - }; }); }); - }; - - const handleFilterChange = (e, { value }) => { - this.setState({ - searchText: value, + }, + }).then((response) => { + setRowData((currentRowData) => { + return update(currentRowData, { + [row.serviceName]: { + [row.name]: { + disabled: { $set: response.data.entry[0].content.disabled }, + __toggleDisable: { $set: false }, + }, + }, + }); }); - }; + }); + }; - const handleChange = (e, { value }) => { - this.setState({ - searchType: value, - }); - }; + const handleFilterChange = (e, { value }) => { + setSearchText(value); + }; - const getSearchTypeDropdown = () => { - const { services } = this.unifiedConfigs.pages.inputs; + const handleChange = (e, { value }) => { + setSearchType(value); + }; - let arr = []; - arr = services.map((service) => { - return ( - - ); - }); + const getSearchTypeDropdown = () => { + const { services } = unifiedConfigs.pages.inputs; - arr.unshift(); - return arr; - }; + let arr = []; + arr = services.map((service) => { + return ; + }); - /** - * - * @param {Array} data - * This function will iterate an arrray and match each key-value with the searchText - * It will return a new array which will match with searchText - */ - const findByMatchingValue = (data) => { - const arr = []; - Object.keys(data).forEach((v) => { - let found = false; - Object.keys(data[v]).forEach((vv) => { - if ( - typeof data[v][vv] === 'string' && - data[v][vv].toLowerCase().includes(this.state.searchText.toLowerCase()) - ) { - if (!found) { - arr.push(data[v]); - found = true; - } - } - }); - }); - return arr; - }; + arr.unshift(); + return arr; + }; - const getRowData = () => { - if (this.state.searchType === 'all') { - let arr = []; - Object.keys(rowData).forEach((key) => { - let newArr = []; - if (this.state.searchText && this.state.searchText.length) { - newArr = findByMatchingValue(rowData[key]); - } else { - newArr = Object.keys(rowData[key]).map((val) => rowData[key][val]); + /** + * + * @param {Array} data + * This function will iterate an arrray and match each key-value with the searchText + * It will return a new array which will match with searchText + */ + const findByMatchingValue = (data) => { + const arr = []; + Object.keys(data).forEach((v) => { + let found = false; + Object.keys(data[v]).forEach((vv) => { + if ( + typeof data[v][vv] === 'string' && + data[v][vv].toLowerCase().includes(searchText.toLowerCase()) + ) { + if (!found) { + arr.push(data[v]); + found = true; } - arr = arr.concat(newArr); - }); - return arr; - } - return findByMatchingValue(rowData[this.state.searchType] || []); - }; - - const filteredData = !this.state.loading && getRowData(); + } + }); + }); + return arr; + }; - if (this.state.error.ucc_err_code) { - throw this.state.error; + const getRowData = () => { + if (searchType === 'all') { + let arr = []; + Object.keys(rowData).forEach((key) => { + let newArr = []; + if (searchText && searchText.length) { + newArr = findByMatchingValue(rowData[key]); + } else { + newArr = Object.keys(rowData[key]).map((val) => rowData[key][val]); + } + arr = arr.concat(newArr); + }); + return arr; } + return findByMatchingValue(rowData[searchType]); + }; - return ( - <> - {this.state.loading ? ( - - ) : ( - <> - - - - -
- {filteredData.length} Input - {filteredData.length > 1 && s} - - - - -
-
-
- - - - -
-
+ const filteredData = !loading && getRowData(); - changeStatus(row)} - /> - - )} - - ); + if (errorCode) { + throw ErrorWithCode(error, errorCode); } + + return ( + <> + {loading ? ( + + ) : ( + <> + + + + +
+ {filteredData.length} Input + {filteredData.length > 1 && s} + + + + +
+
+
+ + + + +
+
+ + changeStatus(row)} + /> + + )} + + ); } TableWrapper.propTypes = { @@ -321,4 +277,4 @@ TableWrapper.propTypes = { serviceName: PropTypes.string.isRequired, }; -export default TableWrapper; +export default memo(TableWrapper); diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/context/InputRowContext.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/context/InputRowContext.jsx index 38b6a9d6c..489a35189 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/context/InputRowContext.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/context/InputRowContext.jsx @@ -1,4 +1,4 @@ -import React, { createContext } from 'react'; +import React, { createContext, useState } from 'react'; import PropTypes from 'prop-types'; const InputRowContext = createContext({ @@ -6,31 +6,18 @@ const InputRowContext = createContext({ setRowData: () => {}, }); -export class InputRowContextProvider extends React.PureComponent { - constructor() { - super(); - this.state = { - rowData: {}, - }; - this.setState = this.setState.bind(this); - } +export const InputRowContextProvider = ({ children }) => { + const [rowData, setRowData] = useState({}); - render() { - return ( - - {this.props.children} - - ); - } -} + return ( + + {children} + + ); +}; InputRowContextProvider.propTypes = { - children: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.node), - PropTypes.node - ]).isRequired + children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired, }; export default InputRowContext; diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/errors/errorWithCode.js b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/errors/errorWithCode.js new file mode 100644 index 000000000..c55acf803 --- /dev/null +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/errors/errorWithCode.js @@ -0,0 +1,7 @@ +export class ErrorWithCode extends Error { + constructor(error, errorCode) { + super(error.message); + this.originalError = error; + this.errorCode = errorCode; + } +} diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/entry_page.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/entry_page.jsx index 384aee8f1..d94423023 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/entry_page.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/entry_page.jsx @@ -1,4 +1,4 @@ -import React, { Suspense } from 'react'; +import React from 'react'; import layout from '@splunk/react-page'; import { SplunkThemeProvider } from '@splunk/themes'; import { defaultTheme } from '@splunk/splunk-utils/themes'; @@ -7,10 +7,7 @@ import { StyledContainer } from './EntryPageStyle'; import ConfigManager from '../util/configManager'; import InputPage from './Input/InputPage'; import ConfigurationPage from './Configuration/ConfigurationPage'; -import ErrorBoundary from '../components/ErrorBoundary'; -// const InputPage = React.lazy(() => import('./Input/InputPage')); -// const ConfigurationPage = React.lazy(() => import('./Configuration/ConfigurationPage')); import { InputRowContextProvider } from '../context/InputRowContext'; const defaultThemeSplunkThemeProviderMap = { diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/util/axiosCallWrapper.js b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/util/axiosCallWrapper.js index 022c8cc76..5d4c2bea9 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/util/axiosCallWrapper.js +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/util/axiosCallWrapper.js @@ -1,23 +1,40 @@ import axios from 'axios'; import { CSRFToken, app } from '@splunk/splunk-utils/config'; import { createRESTURL } from '@splunk/splunk-utils/url'; -import { generateEndPointUrl } from './util'; +import { generateEndPointUrl, generateToast } from './util'; /** - * Provides a axios wrapper with applied common options - * @param {string} serviceName service name which is input name or tab name based on the page - * @param {string} endpointUrl rest endpoint path - * @param {object} params object with params as key value pairs - * @param {object} customHeaders extra headers as key value pair - * @param {string} method rest method type + * + * @param {Object} data The object containing required params for request + * @param {string} data.serviceName service name which is input name or tab name based on the page + * @param {string} data.endpointUrl rest endpoint path + * @param {object} data.params object with params as key value pairs + * @param {object} data.customHeaders extra headers as key value pair + * @param {string} data.method rest method type + * @param {string} data.handleError whether or not show toast notifications on failure + * @param {string} data.callbackOnError callback function to execute after handling error. Only executed when handleError is set to true + * @returns */ const axiosCallWrapper = ( - serviceName, - endpointUrl = null, - params = {}, - customHeaders = {}, - method = 'get' + data = { + serviceName: null, + endpointUrl: null, + params: {}, + customHeaders: {}, + method: 'get', + handleError: false, + callbackOnError: () => {}, + } ) => { + const { + serviceName, + endpointUrl, + params, + customHeaders, + method, + handleError, + callbackOnError, + } = data; const endpoint = serviceName ? generateEndPointUrl(serviceName) : endpointUrl; const appData = { app, @@ -44,7 +61,26 @@ const axiosCallWrapper = ( options.params = params; } - return axios(options); + return handleError + ? axios(options).catch((error) => { + let message = ''; + if (error.response) { + // The request was made and the server responded with a status code + message = `Error response received from server: ${error.response.data.messages[0].text}`; + generateToast(message); + } else if (error.request) { + // The request was made but no response was received + message = `No response received while making request to ${endpointUrl}`; + generateToast(message); + } else { + // Something happened in setting up the request that triggered an Error + message = `Error making ${method} request to ${endpointUrl}`; + generateToast(message); + } + callbackOnError(error); + return Promise.reject(error); + }) + : axios(options); }; export { axiosCallWrapper }; From b1ed49bf14149fc806a316e1c1406d3f260891b0 Mon Sep 17 00:00:00 2001 From: harshpatel Date: Mon, 15 Mar 2021 23:29:44 +0530 Subject: [PATCH 05/12] fix: error handling in errorboundary --- .../main/webapp/components/ErrorBoundary.jsx | 5 +--- .../webapp/components/table/TableWrapper.jsx | 25 ++++++++----------- .../src/main/webapp/errors/errorWithCode.js | 7 ------ 3 files changed, 12 insertions(+), 25 deletions(-) delete mode 100644 splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/errors/errorWithCode.js diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ErrorBoundary.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ErrorBoundary.jsx index 0496fe746..99021040a 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ErrorBoundary.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ErrorBoundary.jsx @@ -4,7 +4,6 @@ import Message from '@splunk/react-ui/Message'; import PropTypes from 'prop-types'; import errorCodes from '../constants/errorCodes'; -import ErrorWithCode from '../errors/errorWithCode'; class ErrorBoundary extends React.Component { constructor(props) { @@ -14,9 +13,7 @@ class ErrorBoundary extends React.Component { static getDerivedStateFromError(error) { // Update state so the next render will show the fallback UI. - if (error instanceof ErrorWithCode) { - return { errorCode: error.errorCode }; - } + return { errorCode: error.uccErrorCode }; } componentDidCatch(error, errorInfo) { diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx index 97b1500b4..d6c12ce66 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx @@ -15,7 +15,6 @@ import { import { getUnifiedConfigs, generateToast } from '../../util/util'; import InputRowContext from '../../context/InputRowContext'; import { axiosCallWrapper } from '../../util/axiosCallWrapper'; -import { ErrorWithCode } from '../../errors/errorWithCode'; function TableWrapper({ isInput, serviceName }) { const [loading, setLoading] = useState(true); @@ -23,7 +22,6 @@ function TableWrapper({ isInput, serviceName }) { const [searchType, setSearchType] = useState('all'); const [selecetedPage, setSelectedPage] = useState('10'); const [error, setError] = useState(null); - const [errorCode, setErrorCode] = useState(''); const { rowData, setRowData } = useContext(InputRowContext); @@ -55,7 +53,7 @@ function TableWrapper({ isInput, serviceName }) { unifiedConfigs.pages.inputs.services.forEach((service) => { requests.push( axiosCallWrapper({ - serviceName: service.name, + serviceName: service.name + '1', }) ); }); @@ -64,28 +62,27 @@ function TableWrapper({ isInput, serviceName }) { // eslint-disable-next-line no-shadow .catch((error) => { let message = ''; + let errorCode = ''; if (error.response) { // The request was made and the server responded with a status code message = `Error received from server: ${error.response.data.messages[0].text}`; - setErrorCode('ERR0001'); - generateToast(message); + errorCode = 'ERR0001'; } else if (error.request) { // The request was made but no response was received message = `No response received while making request to input services`; - setErrorCode('ERR0002'); - generateToast(message); + errorCode = 'ERR0002'; } else { // Something happened in setting up the request that triggered an Error message = `Error making request to input services`; - setErrorCode('ERR0003'); - generateToast(message); + errorCode = 'ERR0003'; } - setError(error); + error.uccErrorCode = errorCode; + generateToast(message); setLoading(false); + setError(error); return Promise.reject(error); }) .then((response) => { - // const isNotUndefined = response.every(Boolean); modifyAPIResponse(response.map((res) => res.data.entry)); }); }; @@ -197,13 +194,13 @@ function TableWrapper({ isInput, serviceName }) { }); return arr; } - return findByMatchingValue(rowData[searchType]); + return findByMatchingValue(rowData[searchType] || []); }; const filteredData = !loading && getRowData(); - if (errorCode) { - throw ErrorWithCode(error, errorCode); + if (error?.uccErrorCode) { + throw error; } return ( diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/errors/errorWithCode.js b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/errors/errorWithCode.js deleted file mode 100644 index c55acf803..000000000 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/errors/errorWithCode.js +++ /dev/null @@ -1,7 +0,0 @@ -export class ErrorWithCode extends Error { - constructor(error, errorCode) { - super(error.message); - this.originalError = error; - this.errorCode = errorCode; - } -} From baa54e73f6b8dae3911b251ceda4af22602c981a Mon Sep 17 00:00:00 2001 From: harshpatel Date: Tue, 16 Mar 2021 10:37:15 +0530 Subject: [PATCH 06/12] fix: remove temporary test change for error boundary --- .../src/main/webapp/components/table/TableWrapper.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx index d6c12ce66..ccc2fa4c3 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx @@ -53,7 +53,7 @@ function TableWrapper({ isInput, serviceName }) { unifiedConfigs.pages.inputs.services.forEach((service) => { requests.push( axiosCallWrapper({ - serviceName: service.name + '1', + serviceName: service.name, }) ); }); From f7158bb6c633f2fdb93932b4da259d34f1fd951f Mon Sep 17 00:00:00 2001 From: mamin-crest Date: Tue, 16 Mar 2021 11:44:03 +0530 Subject: [PATCH 07/12] ADDON-34744-Added initial commit for Custom Control in form --- .../main/webapp/components/BaseFormView.jsx | 147 ++++++++++-------- .../main/webapp/components/ControlWrapper.jsx | 74 ++++++--- .../main/webapp/components/CustomControl.jsx | 56 +++---- .../main/webapp/constants/ControlTypeMap.js | 4 +- .../src/main/webapp/constants/modes.js | 4 + 5 files changed, 161 insertions(+), 124 deletions(-) create mode 100644 splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/constants/modes.js diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/BaseFormView.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/BaseFormView.jsx index aa9b05085..cab51b96d 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/BaseFormView.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/BaseFormView.jsx @@ -2,9 +2,13 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Message from '@splunk/react-ui/Message'; import update from 'immutability-helper'; -import CustomControl from './CustomControl'; import ControlWrapper from './ControlWrapper'; import { getUnifiedConfigs } from '../util/util'; +import { + MODE_CLONE, + MODE_CREATE, + MODE_EDIT +} from "../constants/modes"; class BaseFormView extends Component { constructor(props) { @@ -20,6 +24,14 @@ class BaseFormView extends Component { SetState: (state) => { this.setState(state); }, + setErrorFieldMsg:this.setErrorFieldMsg, + clearAllErrorMsg:this.clearAllErrorMsg + }; + + this.utilControlWrapper = { + handleChange:this.handleChange, + addCustomValidator:this.addCustomValidator, + utilCustomFunctions:this.util }; if (props.isInput) { @@ -46,22 +58,30 @@ class BaseFormView extends Component { this.entities.forEach((e) => { const tempEntity = {}; - if (props.mode === 'CREATE') { + if (props.mode === MODE_CREATE) { tempEntity.value = (typeof e.defaultValue !== "undefined")?e.defaultValue:''; tempEntity.display = (typeof e?.options?.display !== "undefined")?e.options.display:true; tempEntity.error = false; + tempEntity.disabled =false; temState[e.field] = tempEntity; - } else if (props.mode === 'EDIT') { + } + else if (props.mode === MODE_EDIT) { tempEntity.value = (typeof props.currentInput[e.field] !== "undefined")? props.currentInput[e.field]:''; tempEntity.display = (typeof e?.options?.display !== "undefined")?e.options.display:true; tempEntity.error = false; + tempEntity.disabled = (typeof e?.options?.disableonEdit !== "undefined")?e.options.disableonEdit:false; temState[e.field] = tempEntity; - } else { + } + else if (props.mode === MODE_CLONE){ tempEntity.value = e.field === 'name' ? '' : props.currentInput[e.field]; tempEntity.display = (typeof e?.options?.display !== "undefined")?e.options.display:true; tempEntity.error = false; + tempEntity.disabled =e.field==='name'; temState[e.field] = tempEntity; } + else{ + throw new Error('Invalid mode :',props.mode); + } }); this.state = { @@ -92,13 +112,17 @@ class BaseFormView extends Component { return false; } } - // To DO :here We will validate data, save data to global state and also to backend + const datadict={}; + Object.keys(this.state.data).forEach( (field)=> { + datadict[field] = this.state.data[field]["value"]; + }); + + const saveSuccess = true; - const dataValues = {}; const returnValue = { result: saveSuccess, - data: dataValues, + data: datadict, }; if (saveSuccess) { @@ -116,9 +140,9 @@ class BaseFormView extends Component { handleChange = (field, targetValue)=> { - this.clearErrorMsg(); const newFields = update(this.state ,{ data: { [field] : { value: {$set: targetValue } } } } ); - this.setState(newFields); + const tempState = this.clearAllErrorMsg(newFields); + this.setState(tempState); if (this.hookDeferred) { this.hookDeferred.then(() => { @@ -129,12 +153,25 @@ class BaseFormView extends Component { } } + addCustomValidator = (field,validator) =>{ + const index = this.entities.findIndex(x => x.field ===field); + this.entities[index].CustomValidator = validator; + } + // Set error message to display and set error in perticular field + setErrorFieldMsg = (field, msg) =>{ + const newFields = update(this.state ,{ data: { [field] : { error: {$set: true } } } } ); + newFields.ErrorMsg = msg; + this.setState(newFields); + } + + // Set error in perticular field setErrorField = (field) =>{ const newFields = update(this.state ,{ data: { [field] : { error: {$set: true } } } } ); this.setState(newFields); } + // Clear error message clearErrorMsg = () =>{ if(this.state.ErrorMsg){ const newFields = { ...this.state }; @@ -143,37 +180,39 @@ class BaseFormView extends Component { } } + // Set error message setErrorMsg = (msg) =>{ const newFields = { ...this.state }; newFields.ErrorMsg = msg; this.setState(newFields); } - - // Not tested yet - clearAllErrorMsg = () =>{ - const newFields = { ...this.state }; + // Clear error message and errors from fields + clearAllErrorMsg = (State) =>{ + const newFields = State ? { ...State } : {...this.state}; newFields.ErrorMsg = ""; - const newData = {...this.state.data} - - Object.keys(newData).map( (key) => { + const newData = State ? { ...State.data } : {...this.state.data}; + const temData ={} + Object.keys(newData).forEach( (key) => { if(newData[key].error){ const tem = {...newData[key]} tem.error = false; - return tem; + temData[key] = tem; } - return newData[key]; - }); - - newFields.data = newData; - this.setState(newFields); + else{ + temData[key] = newData[key]; + } + }); + newFields.data = temData; + return State ? newFields : null; } + // Display error message generateErrorMessage = () => { if (this.state.ErrorMsg) { return ( -
- +
+ {this.state.ErrorMsg}
@@ -203,7 +242,7 @@ class BaseFormView extends Component { }); } - if (this.props.mode === 'EDIT') { + if (this.props.mode === MODE_EDIT) { if (this.hookDeferred) { this.hookDeferred.then(() => { if (typeof this.hook.onCreate === 'function') { @@ -215,46 +254,28 @@ class BaseFormView extends Component { this.flag = false; } - const rows = []; + return( +
+ {this.generateErrorMessage()} + { + this.entities.map( (e) => { - this.entities.forEach( (e) => { - if (e.type === 'custom') { - rows.push( - - ); - } else { - rows.push( - - ); + const temState = this.state.data[e.field]; + + return ( ) + + }) } - }); - - return(
- {this.generateErrorMessage()} - {rows}
); } diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ControlWrapper.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ControlWrapper.jsx index 1065c92d8..a8b5aef54 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ControlWrapper.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ControlWrapper.jsx @@ -7,7 +7,7 @@ class ControlWrapper extends React.PureComponent { constructor(props){ super(props); - this.controlType = this.isString(props.type) ? CONTROL_TYPE_MAP[props.type] : props.type; + this.controlType = this.isString(props.entity.type) ? CONTROL_TYPE_MAP[props.entity.type] : props.entity.type; } isString = (str)=>{ @@ -15,42 +15,66 @@ class ControlWrapper extends React.PureComponent { } render(){ - const isDisable = this.props.mode ==="EDIT" ? this.props.controlOptions.disableonEdit : false; - const rowView = this.controlType ? ( - React.createElement(this.controlType, - { - handleChange:this.props.handleChange, - value:this.props.value, - field:this.props.field, - disabled:isDisable, - controlOptions:this.props.controlOptions, - })): `No View Found for ${this.props.type} type`; + + const {field, controlOptions, type,label,tooltip, helptext,encrypted=false} = this.props.entity; + const {handleChange, addCustomValidator, utilCustomFunctions} = this.props.utilityFuncts; + let rowView; + if(this.props.entity.type==="custom"){ + + const data = { + value:this.props.value, + mode:this.props.mode, + serviceName:this.props.serviceName + } + rowView = this.controlType ? ( + React.createElement(this.controlType, + { + data, + field, + handleChange, + addCustomValidator, + utilCustomFunctions, + controlOptions + }) + ): `No View Found for ${type} type`; + } + else{ + rowView = this.controlType ? ( + React.createElement(this.controlType, + { + handleChange, + value:this.props.value, + field, + controlOptions, + error:this.props.error, + disabled:this.props.disabled, + encrypted + }) + ): `No View Found for ${type} type`; + } return ( this.props.display && - - {rowView} - + + {rowView} + ) } } ControlWrapper.propTypes = { - tooltip:PropTypes.string, mode:PropTypes.string, - label:PropTypes.string, - handleChange:PropTypes.func, + utilityFuncts:PropTypes.object, value : PropTypes.any, display : PropTypes.bool, error : PropTypes.bool, - helptext : PropTypes.string, - field : PropTypes.string, - type : PropTypes.string, - controlOptions : PropTypes.object + entity : PropTypes.object, + disabled : PropTypes.bool, + serviceName: PropTypes.string } export default ControlWrapper; \ No newline at end of file diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/CustomControl.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/CustomControl.jsx index faffcd69a..29b261df1 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/CustomControl.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/CustomControl.jsx @@ -1,32 +1,31 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import ControlGroup from '@splunk/react-ui/ControlGroup'; +import { getUnifiedConfigs } from '../util/util'; class CustomControl extends Component { componentDidMount() { - const data = { - "mode":this.props.mode, - "field":this.props.field, - "value":this.props.value - } - this.loadCustomControl(this.props.controlOptions.src).then((Control)=>{ - const customControl = new Control(this.el, this.setValue, data); + + const globalConfig = getUnifiedConfigs(); + const appName = globalConfig.meta.name; + + this.loadCustomControl(this.props.controlOptions.src,appName).then((Control)=>{ + const customControl = new Control(globalConfig, this.el,this.props.data,this.setValue,this.props.utilCustomFunctions); customControl.render(); - }) - } + + if(typeof customControl.validation === 'function') { + this.props.addCustomValidator(this.props.field,customControl.validator); + }; + }) + } shouldComponentUpdate() { return false; - } - - componentWillUnmount() { - // destroy el plugin to avoid memmory leak - } + } - loadCustomControl = (module)=> { + loadCustomControl = (module,appName)=> { const myPromise = new Promise((myResolve) => { - __non_webpack_require__([`app/${this.props.appName}/js/build/custom/${module}`], (Control) => { + __non_webpack_require__([`app/${appName}/js/build/custom/${module}`], (Control) => { myResolve(Control); } ); @@ -35,36 +34,23 @@ class CustomControl extends Component { }; setValue = (newValue) => { - this.props.handleChange(this.props.id,newValue); + this.props.handleChange(this.props.field,newValue); } render(){ return ( - this.props.display && -
{this.el = el} } /> - ) } } CustomControl.propTypes = { - tooltip:PropTypes.string, - mode:PropTypes.string, - label:PropTypes.string, - id:PropTypes.number, + data:PropTypes.object, + field:PropTypes.string, handleChange:PropTypes.func, - value : PropTypes.string, - display : PropTypes.bool, - error : PropTypes.bool, - helptext : PropTypes.string, - field : PropTypes.string, controlOptions : PropTypes.object, - appName:PropTypes.string + addCustomValidator:PropTypes.func, + utilCustomFunctions: PropTypes.object } export default CustomControl; \ No newline at end of file diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/constants/ControlTypeMap.js b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/constants/ControlTypeMap.js index a446e1471..a3f37c74b 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/constants/ControlTypeMap.js +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/constants/ControlTypeMap.js @@ -4,6 +4,7 @@ import SingleInputComponent from '../components/SingleInputComponent'; import MultiInputComponent from '../components/MultiInputComponent'; import CheckBoxComponent from '../components/CheckBoxComponent'; import RadioComponent from '../components/RadioComponent'; +import CustomControl from '../components/CustomControl'; export default { 'text': TextComponent, @@ -11,5 +12,6 @@ export default { 'helpLink':Link, 'multipleSelect':MultiInputComponent, 'checkbox':CheckBoxComponent, - 'radio':RadioComponent + 'radio':RadioComponent, + 'custom':CustomControl }; diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/constants/modes.js b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/constants/modes.js new file mode 100644 index 000000000..a65553df3 --- /dev/null +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/constants/modes.js @@ -0,0 +1,4 @@ +export const MODE_CLONE = 'clone'; +export const MODE_CREATE = 'create'; +export const MODE_DELETE = 'delete'; +export const MODE_EDIT = 'edit'; \ No newline at end of file From 86fffbeff96f70666b2b826264d676496d1d2b06 Mon Sep 17 00:00:00 2001 From: Harsh Patel Date: Tue, 16 Mar 2021 09:09:58 +0000 Subject: [PATCH 08/12] fix: filter issue in table --- .../src/main/webapp/components/table/TableWrapper.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx index b2d3aa94b..20a35bf69 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx @@ -204,7 +204,7 @@ function TableWrapper({ isInput, serviceName }) { const [filteredData, totalElement] = getRowData(); - const TableHeaderComponent = () => { + const tableHeaderComponent = () => { return ( - + {tableHeaderComponent()} Date: Tue, 16 Mar 2021 10:22:59 +0000 Subject: [PATCH 09/12] fix: internationalization fixes and code deduplication --- .../main/webapp/components/ErrorBoundary.jsx | 30 +++++++------------ .../webapp/components/table/CustomTable.jsx | 5 ++-- .../webapp/components/table/TableWrapper.jsx | 7 +++-- .../src/main/webapp/pages/Input/InputPage.jsx | 4 +-- .../src/main/webapp/util/axiosCallWrapper.js | 4 +-- 5 files changed, 21 insertions(+), 29 deletions(-) diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ErrorBoundary.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ErrorBoundary.jsx index 99021040a..e9c038217 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ErrorBoundary.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ErrorBoundary.jsx @@ -1,7 +1,9 @@ import React from 'react'; +import PropTypes from 'prop-types'; + import Heading from '@splunk/react-ui/Heading'; import Message from '@splunk/react-ui/Message'; -import PropTypes from 'prop-types'; +import { _ } from '@splunk/ui-utils/i18n'; import errorCodes from '../constants/errorCodes'; @@ -26,31 +28,21 @@ class ErrorBoundary extends React.Component { } render() { - if (this.state.errorCode) { + if (this.state.error) { // Error path return ( <> - Something went wrong! (ERROR_CODE: {this.state.errorCode}) + {_('Something went wrong!')} + {this.state.errorCode ? ` ERROR_CODE: ${this.state.errorCode}` : null} - {errorCodes[this.state.errorCode]} -
- {this.state.error && this.state.error.toString()} -
- {this.state.errorInfo && this.state.errorInfo.componentStack} -
- - ); - } - - if (this.state.error) { - return ( - <> - Something went wrong! + {this.state.errorCode ? ( + {errorCodes[this.state.errorCode]} + ) : null}
- {this.state.error && this.state.error.toString()} + {this.state.error?.toString()}
- {this.state.errorInfo && this.state.errorInfo.componentStack} + {this.state.errorInfo?.componentStack}
); diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/CustomTable.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/CustomTable.jsx index cde30af02..2a6d6ef6c 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/CustomTable.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/CustomTable.jsx @@ -19,6 +19,7 @@ function CustomTable({ isInput, serviceName, data, handleToggleActionClick }) { const [sortDir, setSortDir] = useState('asc'); const unifiedConfigs = getUnifiedConfigs(); const { moreInfo } = unifiedConfigs.pages.inputs.table; + // TODO: add multi field mapping support const statusMapping = moreInfo.filter((a) => a.mapping); const generateColumns = () => { @@ -130,8 +131,8 @@ function CustomTable({ isInput, serviceName, data, handleToggleActionClick }) { disabled={row.__toggleDisable} appearance="toggle" style={{ padding: 0 }} - selectedLabel="Enabled" - unselectedLabel="Disabled" + selectedLabel={_(statusMapping[0].mapping.false)} + unselectedLabel={_(statusMapping[0].mapping.true)} > {statusContent} diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx index 20a35bf69..05f6eb4fc 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/table/TableWrapper.jsx @@ -6,6 +6,7 @@ import PropTypes from 'prop-types'; import ColumnLayout from '@splunk/react-ui/ColumnLayout'; import Select from '@splunk/react-ui/Select'; import Paginator from '@splunk/react-ui/Paginator'; +import { _ } from '@splunk/ui-utils/i18n'; import TableFilter from './TableFilter'; import CustomTable from './CustomTable'; @@ -148,7 +149,7 @@ function TableWrapper({ isInput, serviceName }) { return ; }); - arr.unshift(); + arr.unshift(); return arr; }; @@ -217,8 +218,8 @@ function TableWrapper({ isInput, serviceName }) {
- {totalElement} Input - {totalElement > 1 && s} + {totalElement} + {totalElement > 1 ? _(' Inputs') : _(' Input')} { - console.log("On create new", value); - }}> - {getSearchTypeDropdown()} - + + + { + const findname = services[services.findIndex(x => x.title ===event.target.innerText)].name; + setserviceLabel(`Add ${event.target.innerText}`) + setserviceName(findname); + handleRequestOpen(); + } +} > + {getInputMenu()} + + + } {services && services.length === 1 && @@ -54,7 +92,9 @@ function InputPage({ isInput, serviceName }) { label="Create New Input" appearance="flat" onClick={() => { - console.log("On create new", services[0].name); + setserviceName(services[0].name) + setserviceLabel(`Add ${services[0].title}`) + handleRequestOpen(); }} /> } @@ -62,16 +102,12 @@ function InputPage({ isInput, serviceName }) { - + + {generateModalDialog()} ); } -InputPage.propTypes = { - isInput: PropTypes.bool, - serviceName: PropTypes.string.isRequired, -}; - export default InputPage; diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/entry_page.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/entry_page.jsx index 584480d6a..2c075042c 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/entry_page.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/entry_page.jsx @@ -40,7 +40,7 @@ const page = urlParts[urlParts.length - 1]; if (page === 'inputs') { layout( - , + , { pageTitle: 'Inputs' } ); } else if (page === 'configuration') { From a5ba9c26e3fb5354a52008a829b88be88b8f4dd1 Mon Sep 17 00:00:00 2001 From: mamin-crest Date: Wed, 17 Mar 2021 07:48:43 +0000 Subject: [PATCH 11/12] removed internationalization --- .../src/main/webapp/components/ControlWrapper.jsx | 1 - .../ucc_ui_lib/src/main/webapp/pages/Input/InputPage.jsx | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ControlWrapper.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ControlWrapper.jsx index 371ea3d92..9032b6791 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ControlWrapper.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/components/ControlWrapper.jsx @@ -19,7 +19,6 @@ class ControlWrapper extends React.PureComponent { const {field, options, type,label,tooltip, helptext,encrypted=false} = this.props.entity; const {handleChange, addCustomValidator, utilCustomFunctions} = this.props.utilityFuncts; - console.log("options :",options); let rowView; if(this.props.entity.type==="custom"){ diff --git a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/Input/InputPage.jsx b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/Input/InputPage.jsx index 60017a547..58deff191 100644 --- a/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/Input/InputPage.jsx +++ b/splunk_add_on_ucc_framework/ucc_ui_lib/src/main/webapp/pages/Input/InputPage.jsx @@ -26,8 +26,8 @@ function InputPage() { const toggle =