Skip to content

Commit

Permalink
implement group list
Browse files Browse the repository at this point in the history
Signed-off-by: Harsh Modi <[email protected]>
  • Loading branch information
hjmodi committed Jan 18, 2024
1 parent 18e8a65 commit 14c6381
Show file tree
Hide file tree
Showing 3 changed files with 234 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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}) => <div className="left-half">
<label>Local URL:</label>{' '}
<a href={Utils.storeHref(storeKey)} target="_new">{Utils.storeHref(storeKey)}</a>
</div>;
const LocalURLSection = ({ storeKey }) => <div className="left-half">
<label>Local URL:</label>{' '}
<a href={Utils.storeHref(storeKey)} target="_new">{Utils.storeHref(storeKey)}</a>
</div>;

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 <div className={styleClass}>
<label>Capabilities:</label>{' '}
{
options.map(option => <div key={option.title} className="options">
<span className="key">{option.icon} </span>
</div>)
<span className="key">{option.icon} </span>
</div>)
}
</div>;
}
return <Fragment/>;
return <Fragment />;
};

CapabilitiesSection.propTypes = {
store: PropTypes.object
};

const StoreNameSection = ({store, storeClass}) => <div className="fieldset-caption">
<Link to={`/${store.type}/${store.packageType}/view/${store.name}`}>
<span className={storeClass}>{store.packageType}-{store.name}</span>
</Link>
const ConstituentsSection = ({ constituents }) => {
const [showConstituents, setShowConstituents] = useState(false);

return <div className="right-half">
<div className="inline-label">
{constituents.length} Constituent(s) [
<span className="option">
<a href="" onClick={e => {
e.preventDefault();
setShowConstituents(!showConstituents);
}}>{showConstituents ? '-' : '+'}</a>
</span>
]
</div>
{
showConstituents &&
<ol className="content-panel item-expanded subsection">
{
constituents.map(item => <li key={`constituent-${item}`} className="detail-value-list-item">
<Link to={Utils.detailHref(item)}>{item}</Link>
{Utils.typeFromKey(item) === "remote" && <div className="subfields">
<span className="description field">(Remote URL: <Link to={Utils.storeHref(item)}>{Utils.storeHref(item)}</Link>)</span>
</div>}
</li>)
}
</ol>
}
</div>;
};

ConstituentsSection.propTypes = {
constituents: PropTypes.array
};

const StoreNameSection = ({ store, storeClass }) => <div className="fieldset-caption">
<Link to={`/${store.type}/${store.packageType}/view/${store.name}`}>
<span className={storeClass}>{store.packageType}-{store.name}</span>
</Link>
</div>;

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 (
<div className="content-panel">
<div className="store-listing">
{
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 (
<div key={store.key} className="store-listing-item">
<StoreNameSection store={store} storeClass={storeClass} />
Expand All @@ -82,7 +116,8 @@ const StoreListingWidget = ({storeList, disableMap, storeType}) => {
<a href={store.url} target="_new">{store.url}</a>
</div>
}
{ storeType === "hosted" && <CapabilitiesSection store={store} />}
{storeType === "hosted" && <CapabilitiesSection store={store} />}
{storeType === "group" && <ConstituentsSection constituents={store.constituents} />}
</div>
{
storeType === "remote" && <div><CapabilitiesSection store={store} /></div>
Expand All @@ -106,4 +141,4 @@ StoreListingWidget.propTypes = {
storeType: PropTypes.string
};

export {StoreListingWidget};
export { StoreListingWidget };
109 changes: 105 additions & 4 deletions src/main/webui/src/app/components/content/group/GroupList.jsx
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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 <div>This is not implemented yet!</div>;
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 <LoadingSpiner />;
}

return (
<React.Fragment>
<ListControl
type="group"
legends={options}
handleSearch={event => handlers.handleSearch(event, state.rawList, setState)
}
handleDebug={event => handlers.handleDebug(event, setState)}
handleSortBy={event => handlers.handleSortBy(event, state.rawList, setState)
}
/>
{state.listing ?
<StoreListingWidget
storeList={state.listing}
disableMap={state.disabledMap}
storeType="group"
/>
:
<div className="container-fluid">No content fetched!</div>
}
<ListJsonDebugger
enableDebug={state.enableDebug}
jsonObj={state.listing}
/>
</React.Fragment>
);
}
71 changes: 71 additions & 0 deletions src/main/webui/src/app/components/content/group/GroupList.test.jsx
Original file line number Diff line number Diff line change
@@ -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(<MemoryRouter initialEntries={["/group/maven"]}>
<Routes>
<Route path="/group/:packageType" element={<GroupList />} />
</Routes>
</MemoryRouter>);

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"));
});
});
});

0 comments on commit 14c6381

Please sign in to comment.