diff --git a/src/main/webui/.eslintrc.json b/src/main/webui/.eslintrc.json index b432c40..f91204b 100644 --- a/src/main/webui/.eslintrc.json +++ b/src/main/webui/.eslintrc.json @@ -17,7 +17,8 @@ }, "plugins": [ "react", - "jest" + "jest", + "react-hooks" ], "settings": { "react": { @@ -295,6 +296,8 @@ "yoda": [ 2, "never" - ] + ], + "react-hooks/rules-of-hooks": 1, + "react-hooks/exhaustive-deps": 1 } } diff --git a/src/main/webui/package-lock.json b/src/main/webui/package-lock.json index 78cec39..fc56cff 100644 --- a/src/main/webui/package-lock.json +++ b/src/main/webui/package-lock.json @@ -36,6 +36,7 @@ "eslint": "^8.51.0", "eslint-plugin-jest": "^27.6.0", "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.6.0", "eslint-webpack-plugin": "^4.0.1", "express": "^4.18.2", "fetch-mock": "^9.11.0", @@ -6331,6 +6332,18 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, "node_modules/eslint-plugin-react/node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", diff --git a/src/main/webui/package.json b/src/main/webui/package.json index 1f6f2ef..13abc23 100644 --- a/src/main/webui/package.json +++ b/src/main/webui/package.json @@ -45,6 +45,7 @@ "eslint": "^8.51.0", "eslint-plugin-jest": "^27.6.0", "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.6.0", "eslint-webpack-plugin": "^4.0.1", "express": "^4.18.2", "fetch-mock": "^9.11.0", diff --git a/src/main/webui/src/app/components/content/common/PackageTypeSelect.jsx b/src/main/webui/src/app/components/content/common/PackageTypeSelect.jsx index 17a8cfd..90661b6 100644 --- a/src/main/webui/src/app/components/content/common/PackageTypeSelect.jsx +++ b/src/main/webui/src/app/components/content/common/PackageTypeSelect.jsx @@ -24,21 +24,18 @@ export const PackageTypeSelect = ({packageType,vauleChangeHandler}) =>{ pkgTypes: [] }); - (function() { - const typeUrl = '/api/stats/package-type/keys'; - useEffect(()=>{ - const fetchPkgTypes = async () =>{ - const response = await jsonRest.get(typeUrl); - if (response.ok){ - const pkgTypes = await response.json(); - setState({pkgTypes}); - }else{ - Utils.logMessage(response); - } - }; - fetchPkgTypes(); - }, []); - }()); + useEffect(()=>{ + const fetchPkgTypes = async () =>{ + const response = await jsonRest.get('/api/stats/package-type/keys'); + if (response.ok){ + const pkgTypes = await response.json(); + setState({pkgTypes}); + }else{ + Utils.logMessage(response); + } + }; + fetchPkgTypes(); + }, []); const selectedValue = packageType || "maven"; const onChangeHandler = vauleChangeHandler || (()=>{}); diff --git a/src/main/webui/src/app/components/content/remote/RemoteEdit.jsx b/src/main/webui/src/app/components/content/remote/RemoteEdit.jsx index 7a10c4d..46da52d 100644 --- a/src/main/webui/src/app/components/content/remote/RemoteEdit.jsx +++ b/src/main/webui/src/app/components/content/remote/RemoteEdit.jsx @@ -26,49 +26,6 @@ import {TimeUtils} from '#utils/TimeUtils.js'; import {jsonRest} from '#utils/RestClient.js'; import {STORE_API_BASE_URL} from "../../ComponentConstants.js"; -const init = (pkgType, storeName, setState) => { - const getUrl = `${STORE_API_BASE_URL}/${pkgType}/remote/${storeName}`; - useEffect(()=>{ - const fetchStore = async () =>{ - // get Store data - const response = await jsonRest.get(getUrl); - if (response.ok){ - const raw = await response.json(); - const storeView = Utils.cloneObj(raw); - storeView.disabled = raw.disabled === undefined ? false : raw.disabled; - storeView.useX509 = raw.server_certificate_pem || raw.key_certificate_pem; - storeView.useProxy = raw.proxy_host && true; - // eslint-disable-next-line no-extra-parens - storeView.useAuth = (storeView.useProxy && storeView.proxy_user) || storeView.user; - - // get Store disablement data - const timeoutUrl = `/api/admin/schedule/store/${storeView.packageType}/${storeView.type}/${storeView.name}/disable-timeout`; - const timeoutResponse = await jsonRest.get(timeoutUrl); - const cloned = Utils.cloneObj(storeView); - if (timeoutResponse.ok){ - const timeout = await timeoutResponse.json(); - cloned.disableExpiration = timeout.expiration; - }else{ - response.text().then(error=>Utils.logMessage(`disable timeout getting failed! Error reason: ${error}`)); - } - - // Change state and re-rendering - setState({ - storeView: cloned, - store: raw - }); - }else{ - // TODO: find another way to do error handling - response.text().then(error=>setState({ - message: error - })); - } - }; - - fetchStore(); - }, []); -}; - const CertificateSection = ({store, handleValueChange}) =>
{ store.useAuth && @@ -113,17 +70,54 @@ export default function RemoteEdit() { storeView: {} }); const location = useLocation(); + const {packageType, name} = useParams(); const path = location.pathname; const mode = path.match(/.*\/new$/u)? 'new':'edit'; let [pkgType, storeName] = ["",""]; // Give a default packageType let store = {"packageType": "maven", "type": "remote"}; - if(mode === 'edit'){ - const {packageType, name} = useParams(); - [pkgType, storeName] = [packageType, name]; - init(pkgType, storeName, setState); - store = state.store; - } + [pkgType, storeName] = [packageType, name]; + useEffect(()=>{ + if(mode === 'edit'){ + const fetchStore = async () =>{ + // get Store data + const response = await jsonRest.get(`${STORE_API_BASE_URL}/${pkgType}/remote/${storeName}`); + if (response.ok){ + const raw = await response.json(); + const storeView = Utils.cloneObj(raw); + storeView.disabled = raw.disabled === undefined ? false : raw.disabled; + storeView.useX509 = raw.server_certificate_pem || raw.key_certificate_pem; + storeView.useProxy = raw.proxy_host && true; + // eslint-disable-next-line no-extra-parens + storeView.useAuth = (storeView.useProxy && storeView.proxy_user) || storeView.user; + // get Store disablement data + const timeoutUrl = `/api/admin/schedule/store/${storeView.packageType}/${storeView.type}/${storeView.name}/disable-timeout`; + const timeoutResponse = await jsonRest.get(timeoutUrl); + const cloned = Utils.cloneObj(storeView); + if (timeoutResponse.ok){ + const timeout = await timeoutResponse.json(); + cloned.disableExpiration = timeout.expiration; + }else{ + response.text().then(error=>Utils.logMessage(`disable timeout getting failed! Error reason: ${error}`)); + } + // Change state and re-rendering + setState({ + storeView: cloned, + store: raw + }); + }else{ + // TODO: find another way to do error handling + response.text().then(error=>setState({ + message: error + })); + } + }; + + fetchStore(); + } + }, [pkgType, storeName, mode]); + + store = state.store; const handleCheckChange = (event, field) => { if (event.target.checked) { diff --git a/src/main/webui/src/app/components/content/remote/RemoteList.jsx b/src/main/webui/src/app/components/content/remote/RemoteList.jsx index ff3dc86..2c57ddc 100644 --- a/src/main/webui/src/app/components/content/remote/RemoteList.jsx +++ b/src/main/webui/src/app/components/content/remote/RemoteList.jsx @@ -23,8 +23,30 @@ import {StoreListingWidget} from '../common/StoreListingWidget.jsx'; import {Utils} from '#utils/AppUtils.js'; import {jsonRest} from '#utils/RestClient.js'; +const handlers = { + handleDebug: (event, setState) => { + setState({ + enableDebug: event.target.checked + }); + }, + handleSearch: (event, rawList, setState) => { + setState({ + rawList, + listing: Utils.searchByKeyForNewStores(event.target.value, rawList) + }); + } +}; + +export default function RemoteList() { + const {packageType} = useParams(); + const [state, setState] = useState({ + rawList: [], + listing: [], + disabledMap: {}, + enableDebug: false, + message: '' + }); -const init = (packageType, setState) => { useEffect(()=>{ const fetchdData = async ()=>{ const response = await jsonRest.get(`${STORE_API_BASE_URL}/${packageType}/remote`); @@ -42,6 +64,7 @@ const init = (packageType, setState) => { data = JSON.parse(data); } setState({ + rawList: data.items, listing: data.items, disabledMap }); @@ -55,44 +78,18 @@ const init = (packageType, setState) => { }; fetchdData(); }, [packageType]); -}; - -const handlers = { - handleDebug: (event, setState) => { - setState({ - enableDebug: event.target.checked - }); - }, - handleSearch: (event, rawList, setState) => { - setState({ - listing: Utils.searchByKeyForNewStores(event.target.value, rawList) - }); - } -}; - -export default function RemoteList() { - const {packageType} = useParams(); - const [state, setState] = useState({ - listing: [], - disabledMap: {}, - enableDebug: false, - message: '' - }); - init(packageType,setState); - const listing = state.listing; - const disMap = state.disabledMap; return ( handlers.handleSearch(event, state.listing, setState)} + handleSearch={event => handlers.handleSearch(event, state.rawList, setState)} handleDebug={event => handlers.handleDebug(event, setState)} /> { - listing? - : + state.listing? + :
No content fetched!
diff --git a/src/main/webui/src/app/components/content/remote/RemoteView.jsx b/src/main/webui/src/app/components/content/remote/RemoteView.jsx index e49a389..3f50d18 100644 --- a/src/main/webui/src/app/components/content/remote/RemoteView.jsx +++ b/src/main/webui/src/app/components/content/remote/RemoteView.jsx @@ -28,43 +28,6 @@ import {TimeUtils} from '#utils/TimeUtils.js'; import {jsonRest} from '#utils/RestClient.js'; import {STORE_API_BASE_URL} from '../../ComponentConstants.js'; -const init = (pkgType, storeName, setState) => { - const storeUrl = `${STORE_API_BASE_URL}/${pkgType}/remote/${storeName}`; - useEffect(()=>{ - const fetchStore = async () => { - const response = await jsonRest.get(storeUrl); - if (response.ok){ - const raw = await response.json(); - const store = Utils.cloneObj(raw); - store.disabled = raw.disabled === undefined ? false : raw.disabled; - store.useX509 = raw.server_certificate_pem || raw.key_certificate_pem; - store.useProxy = raw.proxy_host && true; - store.useAuth = store.useProxy && store.proxy_user; - store.useAuth = store.useAuth || store.user; - - // get Store disablement data - const timeoutUrl = `/api/admin/schedule/store/${store.packageType}/${store.type}/${store.name}/disable-timeout`; - const timeoutResponse = await jsonRest.get(timeoutUrl); - const newStore = Utils.cloneObj(store); - if(timeoutResponse.ok){ - const timeoutData = await timeoutResponse.json(); - newStore.disableExpiration = timeoutData.expiration; - } - // Change state and re-rendering - setState({ - store: newStore - }); - }else{ - response.text().then(data => { - Utils.logMessage(`Failed to get store data. Error reason: ${response.status}->${data}`); - }); - } - }; - - fetchStore(); - }, []); -}; - const RemoteAccessSection = ({store})=>
@@ -193,7 +156,41 @@ export default function RemoteView() { message: '' }); const {packageType, name} = useParams(); - init(packageType, name, setState); + + useEffect(()=>{ + const fetchStore = async () => { + const response = await jsonRest.get(`${STORE_API_BASE_URL}/${packageType}/remote/${name}`); + if (response.ok){ + const raw = await response.json(); + const store = Utils.cloneObj(raw); + store.disabled = raw.disabled === undefined ? false : raw.disabled; + store.useX509 = raw.server_certificate_pem || raw.key_certificate_pem; + store.useProxy = raw.proxy_host && true; + store.useAuth = store.useProxy && store.proxy_user; + store.useAuth = store.useAuth || store.user; + + // get Store disablement data + const timeoutUrl = `/api/admin/schedule/store/${store.packageType}/${store.type}/${store.name}/disable-timeout`; + const timeoutResponse = await jsonRest.get(timeoutUrl); + const newStore = Utils.cloneObj(store); + if(timeoutResponse.ok){ + const timeoutData = await timeoutResponse.json(); + newStore.disableExpiration = timeoutData.expiration; + } + // Change state and re-rendering + setState({ + store: newStore + }); + }else{ + response.text().then(data => { + Utils.logMessage(`Failed to get store data. Error reason: ${response.status}->${data}`); + }); + } + }; + + fetchStore(); + }, [packageType, name]); + const store = state.store; if(!Utils.isEmptyObj(store)) { return ( diff --git a/src/main/webui/src/app/components/nav/NavFooter.jsx b/src/main/webui/src/app/components/nav/NavFooter.jsx index 114f98a..77b0010 100644 --- a/src/main/webui/src/app/components/nav/NavFooter.jsx +++ b/src/main/webui/src/app/components/nav/NavFooter.jsx @@ -24,26 +24,23 @@ import {Col, Row} from 'react-bootstrap'; export default function NavFooter() { const [state, setState] = useState({stats: {}}); - (function(){ - const versionUrl = `/api/stats/version-info`; - useEffect(()=>{ - const fetchVersion = async () => { - const response = await jsonRest.get(versionUrl); - if (response.ok){ - const raw = await response.json(); - setState({ - stats: raw - }); - }else{ - response.text().then(data => { - Utils.logMessage(`Failed to version info. Error reason: ${response.status}->${data}`); - }); - } - }; + useEffect(()=>{ + const fetchVersion = async () => { + const response = await jsonRest.get(`/api/stats/version-info`); + if (response.ok){ + const raw = await response.json(); + setState({ + stats: raw + }); + }else{ + response.text().then(data => { + Utils.logMessage(`Failed to version info. Error reason: ${response.status}->${data}`); + }); + } + }; - fetchVersion(); - }, []); - }()); + fetchVersion(); + }, []); const stats = state.stats; const gridClass = "col-md-auto border-right border-secondary"; diff --git a/src/main/webui/src/content-browse/DirectoryListing.jsx b/src/main/webui/src/content-browse/DirectoryListing.jsx index 0c99de4..87e5ac3 100644 --- a/src/main/webui/src/content-browse/DirectoryListing.jsx +++ b/src/main/webui/src/content-browse/DirectoryListing.jsx @@ -102,11 +102,16 @@ URLPage.propTypes = { listingData: PropTypes.object.isRequired }; -const init = setState => { - const url =`/api${document.location.pathname}`; +export default function DirectoryListing () { + const [state, setState] = useState({ + error: null, + isLoaded: false, + data: {} + }); + useEffect(()=>{ const fetchData = async () => { - const response = await jsonRest.get(url); + const response = await jsonRest.get(`/api${document.location.pathname}`); if(response.ok){ const data = await response.json(); setState({ @@ -122,16 +127,6 @@ const init = setState => { }; fetchData(); }, []); -}; - -export default function DirectoryListing () { - const [state, setState] = useState({ - error: null, - isLoaded: false, - data: {} - }); - - init(setState); const {error, isLoaded, data} = state; if (error) {