[Admin Portal]: Add resource ID param in the URL (#2640)

This commit is contained in:
Ankur Singh 2023-02-07 18:20:46 -05:00 коммит произвёл GitHub
Родитель 74964218d2
Коммит 06f7b8bc57
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
14 изменённых файлов: 150 добавлений и 26 удалений

Просмотреть файл

@ -27,14 +27,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:
//
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
//
// kclientset, _ := kubernetes.NewForConfig(c)
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
// kclientset, _ := kubernetes.NewForConfig(c)
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
//
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
// correctly.

Просмотреть файл

@ -27,14 +27,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:
//
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
//
// kclientset, _ := kubernetes.NewForConfig(c)
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
// kclientset, _ := kubernetes.NewForConfig(c)
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
//
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
// correctly.

Просмотреть файл

@ -1,10 +1,10 @@
{
"files": {
"main.js": "/static/js/main.66cb7002.js",
"main.js": "/static/js/main.55666f6d.js",
"index.html": "/index.html",
"main.66cb7002.js.map": "/static/js/main.66cb7002.js.map"
"main.55666f6d.js.map": "/static/js/main.55666f6d.js.map"
},
"entrypoints": [
"static/js/main.66cb7002.js"
"static/js/main.55666f6d.js"
]
}

Просмотреть файл

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>ARO Portal</title><script defer="defer" src="/static/js/main.66cb7002.js"></script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>ARO Portal</title><script defer="defer" src="/static/js/main.55666f6d.js"></script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -54,7 +54,8 @@
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
"last 1 safari version",
"last 1 edge version"
]
}
}

Просмотреть файл

@ -139,7 +139,7 @@ export interface IClusterDetail {
clusterName: string
}
function App() {
function App(props: {params: any}) {
const [data, updateData] = useState({ location: "", csrf: "", elevated: false, username: "" })
const [regions, setRegions] = useState<any>([])
const [error, setError] = useState<AxiosResponse | null>(null)
@ -275,6 +275,7 @@ function App() {
sshBox={sshRef}
setCurrentCluster={setCurrentCluster}
csrfTokenAvailable={fetching}
params={props.params}
/>
</Stack.Item>
<Stack.Item grow>

Просмотреть файл

@ -266,6 +266,7 @@ export function ClusterDetailPanel(props: {
return (
<Panel
id="ClusterDetailPanel"
isOpen={isOpen}
type={PanelType.custom}
onDismiss={_dismissPanel}

Просмотреть файл

@ -13,6 +13,10 @@ import {
TooltipHost,
TextField,
Link,
Layer,
Popup,
DefaultButton,
FocusTrapZone,
} from "@fluentui/react"
import {
DetailsList,
@ -21,6 +25,7 @@ import {
IColumn,
IDetailsListStyles,
} from "@fluentui/react/lib/DetailsList"
import { useBoolean } from "@fluentui/react-hooks"
import { FetchClusters } from "./Request"
import { KubeconfigButton } from "./Kubeconfig"
import { AxiosResponse } from "axios"
@ -71,6 +76,58 @@ const separatorStyle = {
},
}
const popupStyles = mergeStyleSets({
root: {
background: 'rgba(0, 0, 0, 0.2)',
bottom: '0',
left: '0',
position: 'fixed',
right: '0',
top: '0',
},
content: {
background: 'white',
left: '50%',
maxWidth: '400px',
padding: '0 2em 2em',
position: 'absolute',
top: '50%',
transform: 'translate(-50%, -50%)',
},
});
const PopupModal = (props: {title: string, text: string, hidePopup: any}) => {
return (
<>
<Layer>
<Popup
className={popupStyles.root}
role="dialog"
aria-modal="true"
onDismiss={props.hidePopup}
enableAriaHiddenSiblings={true}
>
<FocusTrapZone>
<div role="document" className={popupStyles.content}>
<h2>{props.title}</h2>
<p>
{props.text}
</p>
<DefaultButton onClick={() => {
// this is to change the URL in the address bar
window.history.replaceState({}, "", "/v2")
props.hidePopup()
}}>
Close
</DefaultButton>
</div>
</FocusTrapZone>
</Popup>
</Layer>
</>
);
};
interface IClusterListState {
columns: IColumn[]
items: ICluster[]
@ -364,9 +421,11 @@ export function ClusterList(props: {
sshBox: MutableRefObject<any>
setCurrentCluster: any
csrfTokenAvailable: string
params: any
}) {
const [data, setData] = useState<any>([])
const [error, setError] = useState<AxiosResponse | null>(null)
const [isPopupVisible, { setTrue: showPopup, setFalse: hidePopup }] = useBoolean(false);
const state = useRef<ClusterListComponent>(null)
const [fetching, setFetching] = useState("")
@ -406,6 +465,20 @@ export function ClusterList(props: {
setFetching("FETCHING")
FetchClusters().then(onData)
}
if (props.params) {
const resourceID: string = props.params["resourceid"]
const clusterList = data as ICluster[]
const currentCluster = clusterList.find((item): item is ICluster => resourceID === item.resourceId)
if (fetching === "DONE" && !currentCluster) {
showPopup()
return
}
props.setCurrentCluster(currentCluster)
}
}, [data, fetching, setFetching, props.csrfTokenAvailable])
const _items: ICommandBarItemProps[] = [
@ -431,8 +504,11 @@ export function ClusterList(props: {
styles={controlStyles}
/>
<Separator styles={separatorStyle} />
{error && errorBar()}
{isPopupVisible && PopupModal({title: "Resource Not Found", text: "No resource found due to Invalid/Non-existent resource ID in the URL.", hidePopup: hidePopup})}
<ClusterListComponent
items={data}
ref={state} // why do we need ref here?

Просмотреть файл

@ -14,4 +14,19 @@ mergeStyles({
},
})
ReactDOM.render(<App />, document.getElementById("root"))
const searchParams = window.location.search
const getURLParams = () : any => {
let urlParams = {}
const paramStringFromURL = searchParams.split('?')[1];
const paramsArr = paramStringFromURL.split('&');
for (let i = 0; i < paramsArr.length; i++) {
let pair = paramsArr[i].split('=');
urlParams = {[pair[0]]: pair[1]}
}
return urlParams
}
ReactDOM.render(<App params={ searchParams ? getURLParams(): undefined} />, document.getElementById("root"))

Просмотреть файл

@ -244,4 +244,34 @@ var _ = Describe("Admin Portal E2E Testing", func() {
Expect(link.GetAttribute("href")).To(MatchRegexp(`https://([a-z]|[0-9])+\.admin\.aro\.azure\.com`))
}
})
It("Should open an error modal for an invalid resource ID parameter in the URL", func() {
wd.Get(host + "/v2" + "?resourceid=" + "invalidResourceId")
wd.WaitWithTimeout(conditions.ElementIsLocated(selenium.ByCSSSelector, "div[role='document']"), time.Second*3)
errorModal, err := wd.FindElement(selenium.ByCSSSelector, "div[role='document']")
if err != nil {
SaveScreenshotAndExit(wd, err)
}
Expect(errorModal.IsDisplayed()).To(BeTrue())
})
It("Should display the correct cluster detail view for the resource ID parameter in the URL", func() {
wd.Get(host + "/v2" + "?resourceid=" + resourceIDFromEnv())
wd.WaitWithTimeout(conditions.ElementIsLocated(selenium.ByID, "ClusterDetailPanel"), time.Second*3)
detailPanel, err := wd.FindElement(selenium.ByID, "ClusterDetailPanel")
if err != nil {
SaveScreenshotAndExit(wd, err)
}
Expect(detailPanel.IsDisplayed()).To(BeTrue())
elem, err := wd.FindElement(selenium.ByCSSSelector, "div[class='titleText-109']")
if err != nil {
SaveScreenshotAndExit(wd, err)
}
Expect(elem.Text()).To(Equal(clusterName))
})
})