From b30ea131997278c5d065b61740a367b0a093be56 Mon Sep 17 00:00:00 2001 From: harshpatel Date: Fri, 12 Mar 2021 10:43:59 +0530 Subject: [PATCH] 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, + }); +};