From 14c6381d630a2121eacb66c0237c0309ca18f8c9 Mon Sep 17 00:00:00 2001 From: Harsh Modi Date: Mon, 15 Jan 2024 18:03:52 -0500 Subject: [PATCH] implement group list Signed-off-by: Harsh Modi --- .../content/common/StoreListingWidget.jsx | 81 +++++++++---- .../components/content/group/GroupList.jsx | 109 +++++++++++++++++- .../content/group/GroupList.test.jsx | 71 ++++++++++++ 3 files changed, 234 insertions(+), 27 deletions(-) create mode 100644 src/main/webui/src/app/components/content/group/GroupList.test.jsx diff --git a/src/main/webui/src/app/components/content/common/StoreListingWidget.jsx b/src/main/webui/src/app/components/content/common/StoreListingWidget.jsx index 737acd5..054140f 100644 --- a/src/main/webui/src/app/components/content/common/StoreListingWidget.jsx +++ b/src/main/webui/src/app/components/content/common/StoreListingWidget.jsx @@ -14,62 +14,96 @@ * limitations under the License. */ -import React, {Fragment} from 'react'; -import {Link} from 'react-router-dom'; -import {PropTypes} from 'prop-types'; -import {Utils} from '#utils/AppUtils.js'; +import React, { Fragment, useState } from 'react'; +import { Link } from 'react-router-dom'; +import { PropTypes } from 'prop-types'; +import { Utils } from '#utils/AppUtils.js'; -const LocalURLSection = ({storeKey}) =>
- {' '} - {Utils.storeHref(storeKey)} -
; +const LocalURLSection = ({ storeKey }) =>
+ {' '} + {Utils.storeHref(storeKey)} +
; LocalURLSection.propTypes = { storeKey: PropTypes.string }; // For options, see AppUtils.remoteOptions|hostedOptions -const CapabilitiesSection = ({store}) => { - if(store.type==="remote" || store.type==="hosted"){ +const CapabilitiesSection = ({ store }) => { + if (store.type === "remote" || store.type === "hosted") { const options = Utils.storeOptions(store); - const styleClass = store.type==="remote"?"left-half":"right-half"; + const styleClass = store.type === "remote" ? "left-half" : "right-half"; return
{' '} { options.map(option =>
- {option.icon} -
) + {option.icon} +
) } ; } - return ; + return ; }; CapabilitiesSection.propTypes = { store: PropTypes.object }; -const StoreNameSection = ({store, storeClass}) =>
- - {store.packageType}-{store.name} - +const ConstituentsSection = ({ constituents }) => { + const [showConstituents, setShowConstituents] = useState(false); + + return
+ + { + showConstituents && +
    + { + constituents.map(item =>
  1. + {item} + {Utils.typeFromKey(item) === "remote" &&
    + (Remote URL: {Utils.storeHref(item)}) +
    } +
  2. ) + } +
+ }
; +}; + +ConstituentsSection.propTypes = { + constituents: PropTypes.array +}; + +const StoreNameSection = ({ store, storeClass }) =>
+ + {store.packageType}-{store.name} + +
; StoreNameSection.propTypes = { store: PropTypes.object, storeClass: PropTypes.string }; -const StoreListingWidget = ({storeList, disableMap, storeType}) => { +const StoreListingWidget = ({ storeList, disableMap, storeType }) => { const listing = storeList; const disMap = disableMap; - if(listing && listing.length >0){ + if (listing && listing.length > 0) { return (
{ listing.map(store => { - const storeClass = Utils.isDisabled(store.key, disMap)? "disabled-store":"enabled-store"; + const storeClass = Utils.isDisabled(store.key, disMap) ? "disabled-store" : "enabled-store"; return (
@@ -82,7 +116,8 @@ const StoreListingWidget = ({storeList, disableMap, storeType}) => { {store.url}
} - { storeType === "hosted" && } + {storeType === "hosted" && } + {storeType === "group" && }
{ storeType === "remote" &&
@@ -106,4 +141,4 @@ StoreListingWidget.propTypes = { storeType: PropTypes.string }; -export {StoreListingWidget}; +export { StoreListingWidget }; diff --git a/src/main/webui/src/app/components/content/group/GroupList.jsx b/src/main/webui/src/app/components/content/group/GroupList.jsx index f7b207d..98e0fae 100644 --- a/src/main/webui/src/app/components/content/group/GroupList.jsx +++ b/src/main/webui/src/app/components/content/group/GroupList.jsx @@ -1,5 +1,5 @@ /** - * Copyright (C) 2023 Red Hat, Inc. (https://github.com/Commonjava/indy-ui-service) + * Copyright (C) 2024 Red Hat, Inc. (https://github.com/Commonjava/indy-ui-service) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,8 +14,109 @@ * limitations under the License. */ -import React from 'react'; +import React, {useEffect, useState} from "react"; +import {useParams} from "react-router-dom"; +import {ListJsonDebugger} from "../common/Debugger.jsx"; +import ListControl from "../common/ListControl.jsx"; +import {groupOptionLegend as options} from "../../ComponentConstants.js"; +import {StoreListingWidget} from "../common/StoreListingWidget.jsx"; +import {LoadingSpiner} from "../common/LoadingSpiner.jsx"; +import {Utils} from "#utils/AppUtils.js"; +import {IndyRest} from "#utils/RestClient.js"; -export default function GruopList() { - return
This is not implemented yet!
; +const {storeRes, disableRes} = IndyRest; + +const handlers = { + handleDebug: (event, setState) => { + setState({ + enableDebug: event.target.checked, + }); + }, + handleSearch: (event, rawList, setState) => { + setState({ + rawList, + listing: Utils.searchByKeyForNewStores(event.target.value, rawList), + }); + }, + handleSortBy: (event, rawList, setState) => { + setState({ + rawList, + listing: Utils.sortByPropForStores(event.target.value, rawList), + }); + }, +}; + +export default function GroupList() { + const {packageType} = useParams(); + const [state, setState] = useState({ + rawList: [], + listing: [], + disabledMap: {}, + enableDebug: false, + message: "", + }); + const [loading, setLoading] = useState(true); + + useEffect(() => { + setLoading(true); + const fetchdData = async () => { + const res = await storeRes.getStores(packageType, "group"); + if (res.success) { + const timeoutRes = await disableRes.getAllStoreTimeout(); + let disabledMap = {}; + if (timeoutRes.success) { + const timeoutData = timeoutRes.result; + disabledMap = Utils.setDisableMap(timeoutData); + } else { + Utils.logMessage(`disable timeout get failed in group listing! Error reason: ${timeoutRes.error.message}`,); + } + let data = res.result; + if (typeof data === "string") { + data = JSON.parse(data); + } + setState({ + rawList: data.items, + listing: data.items, + disabledMap, + }); + } else { + setState({ + message: res.error.message, + }); + } + setLoading(false); + }; + fetchdData(); + }, [packageType]); + + if (loading) { + return ; + } + + return ( + + handlers.handleSearch(event, state.rawList, setState) + } + handleDebug={event => handlers.handleDebug(event, setState)} + handleSortBy={event => handlers.handleSortBy(event, state.rawList, setState) + } + /> + {state.listing ? + + : +
No content fetched!
+ } + +
+ ); } diff --git a/src/main/webui/src/app/components/content/group/GroupList.test.jsx b/src/main/webui/src/app/components/content/group/GroupList.test.jsx new file mode 100644 index 0000000..88858b8 --- /dev/null +++ b/src/main/webui/src/app/components/content/group/GroupList.test.jsx @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2024 Red Hat, Inc. (https://github.com/Commonjava/indy-ui-service) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from "react"; +import {MemoryRouter, Route, Routes} from 'react-router-dom'; +import {render, screen, cleanup, waitFor} from '@testing-library/react'; +import '@testing-library/jest-dom'; +import fetchMock from "fetch-mock"; +import GroupList from "./GroupList.jsx"; +import {Utils} from "#utils/AppUtils.js"; +import {STORE_API_BASE_URL} from "../../ComponentConstants.js"; + +beforeEach(()=>{ + fetchMock.restore(); +}); + +afterEach(() => { + cleanup(); +}); + +describe('GroupList tests', () => { + it("Verify GroupList", async ()=>{ + const mockGroupStoreList = JSON.stringify({items: [ + {name: "central", type: "group", packageType: "maven", + key: "maven:group:central", disabled: false, "allow_snapshots": true, + "allow_releases": true, url: "https://repo.maven.apache.org/maven2/", + constituents: [], + description: "official maven central"}, + {name: "mrrc", type: "group", packageType: "maven", + key: "maven:group:mrrc", disabled: false, + url: "https://maven.repository.redhat.com/ga/", + constituents: ["maven:remote:central", "maven:hosted:local-deployment"], + description: "Red Hat maven repository"} + ]}); + const mockDisableTimeout = JSON.stringify({items: [ + {name: "Disable-Timeout", group: "maven:group:central#Disable-Timeout", + expiration: "2030-02-22T17:00:00.000Z"}, + {name: "Disable-Timeout", group: "maven:group:mrrc#Disable-Timeout", + expiration: "2030-03-22T17:00:00.000Z"} + ]}); + fetchMock.mock(`${STORE_API_BASE_URL}/maven/group`, {status: 200, body: JSON.stringify(mockGroupStoreList)}); + fetchMock.mock("/api/admin/schedule/store/all/disable-timeout", {status: 200, body: JSON.stringify(mockDisableTimeout)}); + render( + + } /> + + ); + + await waitFor(() => { + // ListControl section testing + expect(screen.getByRole("button", {name: "New..."})).toBeInTheDocument(); + + // StoreListing section testing + expect(screen.getByRole("link", {name: Utils.storeHref("maven:group:central")})).toHaveAttribute("href", Utils.storeHref("maven:group:central")); + expect(screen.getByRole("link", {name: Utils.storeHref("maven:group:mrrc")})).toHaveAttribute("href", Utils.storeHref("maven:group:mrrc")); + }); + }); +});