]> source.dussan.org Git - sonarqube.git/blob
efaea7d8616b655bca4f8d7e2773828786f738ca
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 import React, { useEffect, useState } from 'react';
21 import theme from '../../../../app/theme';
22 import Modal from '../../../../components/controls/Modal';
23 import { Button } from '../../../../components/controls/buttons';
24 import CheckIcon from '../../../../components/icons/CheckIcon';
25 import ClearIcon from '../../../../components/icons/ClearIcon';
26 import { Alert, AlertVariant } from '../../../../components/ui/Alert';
27 import { translate, translateWithParameters } from '../../../../helpers/l10n';
28 import { GitHubProvisioningStatus } from '../../../../types/provisioning';
29 import { useCheckGitHubConfigQuery } from './queries/identity-provider';
30
31 const intlPrefix = 'settings.authentication.github.configuration.validation';
32
33 function ValidityIcon({ valid }: { valid: boolean }) {
34   const color = valid ? theme.colors.success500 : theme.colors.error500;
35
36   return valid ? (
37     <CheckIcon fill={color} label={translate(`${intlPrefix}.details.valid_label`)} />
38   ) : (
39     <ClearIcon fill={color} label={translate(`${intlPrefix}.details.invalid_label`)} />
40   );
41 }
42
43 interface Props {
44   isAutoProvisioning: boolean;
45 }
46
47 function GitHubConfigurationValidity({ isAutoProvisioning }: Props) {
48   const [openDetails, setOpenDetails] = useState(false);
49   const [messages, setMessages] = useState<string[]>([]);
50   const [alertVariant, setAlertVariant] = useState<AlertVariant>('loading');
51   const { data, isFetching, refetch } = useCheckGitHubConfigQuery(true);
52   const modalHeader = translate(`${intlPrefix}.details.title`);
53
54   const applicationField = isAutoProvisioning ? 'autoProvisioning' : 'jit';
55
56   const isValidApp =
57     data?.application[applicationField].status === GitHubProvisioningStatus.Success;
58
59   useEffect(() => {
60     if (isFetching) {
61       setMessages([translate(`${intlPrefix}.loading`)]);
62       setAlertVariant('loading');
63       return;
64     }
65
66     const invalidOrgs =
67       isValidApp && isAutoProvisioning && data
68         ? data.installations.filter(
69             (org) => org.autoProvisioning.status === GitHubProvisioningStatus.Failed
70           )
71         : [];
72
73     if (isValidApp && invalidOrgs.length === 0) {
74       setMessages([
75         translateWithParameters(
76           `${intlPrefix}.valid${data.installations.length === 1 ? '_one' : ''}`,
77           isAutoProvisioning
78             ? translate('settings.authentication.github.form.provisioning_with_github_short')
79             : translate('settings.authentication.form.provisioning_at_login_short'),
80           data.installations.length === 1
81             ? data.installations[0].organization
82             : data.installations.length
83         ),
84       ]);
85       setAlertVariant('success');
86     } else {
87       setMessages([
88         translateWithParameters(
89           `${intlPrefix}.invalid`,
90           data?.application[applicationField].errorMessage ?? ''
91         ),
92         ...invalidOrgs.map((org) =>
93           translateWithParameters(
94             `${intlPrefix}.invalid_org`,
95             org.organization,
96             org.autoProvisioning.errorMessage ?? ''
97           )
98         ),
99       ]);
100       setAlertVariant('error');
101     }
102   }, [isFetching, isValidApp, isAutoProvisioning, applicationField, data]);
103
104   return (
105     <>
106       <Alert title={messages[0]} variant={alertVariant}>
107         <div className="sw-flex sw-justify-between sw-items-center">
108           <div>
109             {messages.map((msg) => (
110               <div key={msg}>{msg}</div>
111             ))}
112           </div>
113           <div>
114             <Button onClick={() => setOpenDetails(true)} disabled={isFetching} className="sw-mr-2">
115               {translate(`${intlPrefix}.details`)}
116             </Button>
117             <Button onClick={() => refetch()} disabled={isFetching}>
118               {translate(`${intlPrefix}.test`)}
119             </Button>
120           </div>
121         </div>
122       </Alert>
123       {openDetails && (
124         <Modal size="small" contentLabel={modalHeader} onRequestClose={() => setOpenDetails(false)}>
125           <header className="modal-head">
126             <h2>
127               {modalHeader} <ValidityIcon valid={isValidApp} />
128             </h2>
129           </header>
130           <div className="modal-body modal-container">
131             {!isValidApp && (
132               <Alert variant="error">{data?.application[applicationField].errorMessage}</Alert>
133             )}
134             <ul className="sw-pl-5">
135               {data?.installations.map((inst) => (
136                 <li key={inst.organization}>
137                   <ValidityIcon
138                     valid={
139                       !isAutoProvisioning ||
140                       inst.autoProvisioning.status === GitHubProvisioningStatus.Success
141                     }
142                   />
143                   <span className="sw-ml-2">{inst.organization}</span>
144                   {isAutoProvisioning &&
145                     inst.autoProvisioning.status === GitHubProvisioningStatus.Failed && (
146                       <span> - {inst.autoProvisioning.errorMessage}</span>
147                     )}
148                 </li>
149               ))}
150             </ul>
151           </div>
152           <footer className="modal-foot">
153             <Button onClick={() => setOpenDetails(false)}>{translate('close')}</Button>
154           </footer>
155         </Modal>
156       )}
157     </>
158   );
159 }
160
161 export default GitHubConfigurationValidity;