mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-03 20:15:52 +01:00
Enable Health Checks (#1621)
In the release of version 2.0.0 of SCM-Manager, the health checks had been neglected. This makes them visible again in the frontend and adds the ability to trigger them. In addition there are two types of health checks: The "normal" ones, now called "light checks", that are run on startup, and more intense checks run only on request. As a change to version 1.x, health checks will no longer be persisted for repositories. Co-authored-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com>
This commit is contained in:
@@ -35,9 +35,19 @@ type Props = {
|
||||
active: boolean;
|
||||
className?: string;
|
||||
headColor?: string;
|
||||
headTextColor?: string;
|
||||
};
|
||||
|
||||
export const Modal: FC<Props> = ({ title, closeFunction, body, footer, active, className, headColor = "light" }) => {
|
||||
export const Modal: FC<Props> = ({
|
||||
title,
|
||||
closeFunction,
|
||||
body,
|
||||
footer,
|
||||
active,
|
||||
className,
|
||||
headColor = "light",
|
||||
headTextColor = "black"
|
||||
}) => {
|
||||
const portalRootElement = usePortalRootElement("modalsRoot");
|
||||
|
||||
if (!portalRootElement) {
|
||||
@@ -56,7 +66,7 @@ export const Modal: FC<Props> = ({ title, closeFunction, body, footer, active, c
|
||||
<div className="modal-background" onClick={closeFunction} />
|
||||
<div className="modal-card">
|
||||
<header className={classNames("modal-card-head", `has-background-${headColor}`)}>
|
||||
<p className="modal-card-title is-marginless">{title}</p>
|
||||
<p className={`modal-card-title is-marginless has-text-${headTextColor}`}>{title}</p>
|
||||
<button className="delete" aria-label="close" onClick={closeFunction} />
|
||||
</header>
|
||||
<section className="modal-card-body">{body}</section>
|
||||
|
||||
60
scm-ui/ui-components/src/repos/HealthCheckFailureDetail.tsx
Normal file
60
scm-ui/ui-components/src/repos/HealthCheckFailureDetail.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React, { FC } from "react";
|
||||
import { Modal } from "../modals";
|
||||
import { HealthCheckFailure } from "@scm-manager/ui-types";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button } from "../buttons";
|
||||
import HealthCheckFailureList from "./HealthCheckFailureList";
|
||||
|
||||
type Props = {
|
||||
active: boolean;
|
||||
closeFunction: () => void;
|
||||
failures?: HealthCheckFailure[];
|
||||
};
|
||||
|
||||
const HealthCheckFailureDetail: FC<Props> = ({ active, closeFunction, failures }) => {
|
||||
const [t] = useTranslation("repos");
|
||||
|
||||
const footer = <Button label={t("healthCheckFailure.close")} action={closeFunction} color="grey" />;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
body={
|
||||
<div className={"content"}>
|
||||
<HealthCheckFailureList failures={failures} />
|
||||
</div>
|
||||
}
|
||||
title={t("healthCheckFailure.title")}
|
||||
closeFunction={closeFunction}
|
||||
active={active}
|
||||
footer={footer}
|
||||
headColor={"danger"}
|
||||
headTextColor={"white"}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default HealthCheckFailureDetail;
|
||||
69
scm-ui/ui-components/src/repos/HealthCheckFailureList.tsx
Normal file
69
scm-ui/ui-components/src/repos/HealthCheckFailureList.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { HealthCheckFailure } from "@scm-manager/ui-types";
|
||||
import React, { FC } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
failures?: HealthCheckFailure[];
|
||||
};
|
||||
|
||||
const HealthCheckFailureList: FC<Props> = ({ failures }) => {
|
||||
const [t] = useTranslation("plugins");
|
||||
|
||||
const translationOrDefault = (translationKey: string, defaultValue: string) => {
|
||||
const translation = t(translationKey);
|
||||
return translation === translationKey ? defaultValue : translation;
|
||||
};
|
||||
|
||||
if (!failures) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const failureLine = (failure: HealthCheckFailure) => {
|
||||
const summary = translationOrDefault(`healthCheckFailures.${failure.id}.summary`, failure.summary);
|
||||
const description = translationOrDefault(`healthCheckFailures.${failure.id}.description`, failure.description);
|
||||
|
||||
return (
|
||||
<li>
|
||||
<em>{summary}</em>
|
||||
<br />
|
||||
{description}
|
||||
<br />
|
||||
{failure.url && (
|
||||
<a href={failure.url} target="_blank" rel="noreferrer">
|
||||
{t("healthCheckFailures.detailUrl")}
|
||||
</a>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
const failureComponents = failures.map(failureLine);
|
||||
|
||||
return <ul>{failureComponents}</ul>;
|
||||
};
|
||||
|
||||
export default HealthCheckFailureList;
|
||||
@@ -29,6 +29,7 @@ import RepositoryAvatar from "./RepositoryAvatar";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import { withTranslation, WithTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import HealthCheckFailureDetail from "./HealthCheckFailureDetail";
|
||||
|
||||
type DateProp = Date | string;
|
||||
|
||||
@@ -39,6 +40,10 @@ type Props = WithTranslation & {
|
||||
baseDate?: DateProp;
|
||||
};
|
||||
|
||||
type State = {
|
||||
showHealthCheck: boolean;
|
||||
};
|
||||
|
||||
const RepositoryTag = styled.span`
|
||||
margin-left: 0.2rem;
|
||||
background-color: #9a9a9a;
|
||||
@@ -50,8 +55,26 @@ const RepositoryTag = styled.span`
|
||||
font-weight: bold;
|
||||
font-size: 0.7rem;
|
||||
`;
|
||||
const RepositoryWarnTag = styled.span`
|
||||
margin-left: 0.2rem;
|
||||
background-color: #f14668;
|
||||
padding: 0.25rem;
|
||||
border-radius: 5px;
|
||||
color: white;
|
||||
overflow: visible;
|
||||
pointer-events: all;
|
||||
font-weight: bold;
|
||||
font-size: 0.7rem;
|
||||
cursor: help;
|
||||
`;
|
||||
|
||||
class RepositoryEntry extends React.Component<Props> {
|
||||
class RepositoryEntry extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showHealthCheck: false
|
||||
};
|
||||
}
|
||||
createLink = (repository: Repository) => {
|
||||
return `/repo/${repository.namespace}/${repository.name}`;
|
||||
};
|
||||
@@ -154,6 +177,19 @@ class RepositoryEntry extends React.Component<Props> {
|
||||
repositoryFlags.push(<RepositoryTag title={t("exporting.tooltip")}>{t("repository.exporting")}</RepositoryTag>);
|
||||
}
|
||||
|
||||
if (repository.healthCheckFailures && repository.healthCheckFailures.length > 0) {
|
||||
repositoryFlags.push(
|
||||
<RepositoryWarnTag
|
||||
title={t("healthCheckFailure.tooltip")}
|
||||
onClick={() => {
|
||||
this.setState({ showHealthCheck: true });
|
||||
}}
|
||||
>
|
||||
{t("repository.healthCheckFailure")}
|
||||
</RepositoryWarnTag>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ExtensionPoint name="repository.card.beforeTitle" props={{ repository }} />
|
||||
@@ -168,16 +204,27 @@ class RepositoryEntry extends React.Component<Props> {
|
||||
const footerLeft = this.createFooterLeft(repository, repositoryLink);
|
||||
const footerRight = this.createFooterRight(repository, baseDate);
|
||||
const title = this.createTitle();
|
||||
return (
|
||||
<CardColumn
|
||||
avatar={<RepositoryAvatar repository={repository} />}
|
||||
title={title}
|
||||
description={repository.description}
|
||||
link={repositoryLink}
|
||||
footerLeft={footerLeft}
|
||||
footerRight={footerRight}
|
||||
const modal = (
|
||||
<HealthCheckFailureDetail
|
||||
closeFunction={() => this.setState({ showHealthCheck: false })}
|
||||
active={this.state.showHealthCheck}
|
||||
failures={repository.healthCheckFailures}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{modal}
|
||||
<CardColumn
|
||||
avatar={<RepositoryAvatar repository={repository} />}
|
||||
title={title}
|
||||
description={repository.description}
|
||||
link={repositoryLink}
|
||||
footerLeft={footerLeft}
|
||||
footerRight={footerRight}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ export { default as RepositoryEntry } from "./RepositoryEntry";
|
||||
export { default as RepositoryEntryLink } from "./RepositoryEntryLink";
|
||||
export { default as JumpToFileButton } from "./JumpToFileButton";
|
||||
export { default as CommitAuthor } from "./CommitAuthor";
|
||||
export { default as HealthCheckFailureDetail } from "./HealthCheckFailureDetail";
|
||||
|
||||
export {
|
||||
File,
|
||||
|
||||
Reference in New Issue
Block a user