From 06a6d3ef03c50f3733023a6eb9869d3f898ee670 Mon Sep 17 00:00:00 2001 From: "Ambroise C." Date: Tue, 30 Jul 2024 14:22:27 +0200 Subject: [PATCH] SONAR-22515 Create email notification tab (#11363) --- .../components/AdditionalCategories.tsx | 14 ++ .../settings/components/AllCategoriesList.tsx | 24 +-- .../js/apps/settings/components/EmailForm.tsx | 169 ------------------ .../settings/components/SettingsSearch.tsx | 4 +- .../components/SubCategoryDefinitionsList.tsx | 20 +-- .../email-notification/EmailForm.tsx | 136 ++++++++++++++ .../email-notification/EmailNotification.tsx | 46 +++++ .../src/main/js/apps/settings/constants.ts | 141 +++++++++++++++ .../src/main/js/apps/settings/utils.ts | 134 -------------- .../sonar-web/src/main/js/queries/emails.ts | 36 ++++ .../resources/org/sonar/l10n/core.properties | 3 + 11 files changed, 398 insertions(+), 329 deletions(-) delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/EmailForm.tsx create mode 100644 server/sonar-web/src/main/js/apps/settings/components/email-notification/EmailForm.tsx create mode 100644 server/sonar-web/src/main/js/apps/settings/components/email-notification/EmailNotification.tsx create mode 100644 server/sonar-web/src/main/js/queries/emails.ts diff --git a/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategories.tsx b/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategories.tsx index ffdf20a0f9d..dc913bcdcee 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategories.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategories.tsx @@ -25,6 +25,7 @@ import { ALM_INTEGRATION_CATEGORY, ANALYSIS_SCOPE_CATEGORY, AUTHENTICATION_CATEGORY, + EMAIL_NOTIFICATION_CATEGORY, LANGUAGES_CATEGORY, NEW_CODE_PERIOD_CATEGORY, PULL_REQUEST_DECORATION_BINDING_CATEGORY, @@ -34,6 +35,7 @@ import Languages from './Languages'; import NewCodeDefinition from './NewCodeDefinition'; import AlmIntegration from './almIntegration/AlmIntegration'; import Authentication from './authentication/Authentication'; +import EmailNotification from './email-notification/EmailNotification'; import PullRequestDecorationBinding from './pullRequestDecorationBinding/PRDecorationBinding'; export interface AdditionalCategoryComponentProps { @@ -103,6 +105,14 @@ export const ADDITIONAL_CATEGORIES: AdditionalCategory[] = [ availableForProject: false, displayTab: false, }, + { + key: EMAIL_NOTIFICATION_CATEGORY, + name: translate('settings.email_notification.category'), + renderComponent: getEmailNotificationComponent, + availableGlobally: true, + availableForProject: false, + displayTab: true, + }, ]; function getLanguagesComponent(props: AdditionalCategoryComponentProps) { @@ -128,3 +138,7 @@ function getAuthenticationComponent(props: AdditionalCategoryComponentProps) { function getPullRequestDecorationBindingComponent(props: AdditionalCategoryComponentProps) { return props.component && ; } + +function getEmailNotificationComponent(props: AdditionalCategoryComponentProps) { + return ; +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx index 64080ba3d7a..ad4cd62e690 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx @@ -55,21 +55,25 @@ function CategoriesList(props: Readonly) { ); const categoriesWithName = categories - .filter((key) => !CATEGORY_OVERRIDES[key.toLowerCase()]) + .filter((key) => CATEGORY_OVERRIDES[key.toLowerCase()] === undefined) .map((key) => ({ key, name: getCategoryName(key), })) .concat( - ADDITIONAL_CATEGORIES.filter((c) => c.displayTab) - .filter((c) => - component - ? // Project settings - c.availableForProject - : // Global settings - c.availableGlobally, - ) - .filter((c) => props.hasFeature(Feature.BranchSupport) || !c.requiresBranchSupport), + ADDITIONAL_CATEGORIES.filter((c) => { + const availableForCurrentMenu = component + ? // Project settings + c.availableForProject + : // Global settings + c.availableGlobally; + + return ( + c.displayTab && + availableForCurrentMenu && + (props.hasFeature(Feature.BranchSupport) || !c.requiresBranchSupport) + ); + }), ); const sortedCategories = sortBy(categoriesWithName, (category) => category.name.toLowerCase()); diff --git a/server/sonar-web/src/main/js/apps/settings/components/EmailForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/EmailForm.tsx deleted file mode 100644 index 4dfa5c97541..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/EmailForm.tsx +++ /dev/null @@ -1,169 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { - BasicSeparator, - ButtonPrimary, - FlagMessage, - FormField, - InputField, - InputTextArea, - Spinner, - SubHeading, -} from 'design-system'; -import * as React from 'react'; -import { sendTestEmail } from '../../../api/settings'; -import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext'; -import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation'; -import { translate, translateWithParameters } from '../../../helpers/l10n'; -import { parseError } from '../../../helpers/request'; -import { LoggedInUser } from '../../../types/users'; - -interface Props { - currentUser: LoggedInUser; -} - -interface State { - error?: string; - loading: boolean; - message: string; - recipient: string; - subject: string; - success?: string; -} - -export class EmailForm extends React.PureComponent { - mounted = false; - - constructor(props: Props) { - super(props); - this.state = { - recipient: this.props.currentUser.email || '', - subject: translate('email_configuration.test.subject'), - message: translate('email_configuration.test.message_text'), - loading: false, - }; - } - - componentDidMount() { - this.mounted = true; - } - - componentWillUnmount() { - this.mounted = false; - } - - handleError = (response: Response) => { - return parseError(response).then((message) => { - if (this.mounted) { - this.setState({ error: message, loading: false }); - } - }); - }; - - handleFormSubmit = (event: React.FormEvent) => { - event.preventDefault(); - this.setState({ success: undefined, error: undefined, loading: true }); - const { recipient, subject, message } = this.state; - sendTestEmail(recipient, subject, message).then(() => { - if (this.mounted) { - this.setState({ success: recipient, loading: false }); - } - }, this.handleError); - }; - - onRecipientChange = (event: React.ChangeEvent) => { - this.setState({ recipient: event.target.value }); - }; - - onSubjectChange = (event: React.ChangeEvent) => { - this.setState({ subject: event.target.value }); - }; - - onMessageChange = (event: React.ChangeEvent) => { - this.setState({ message: event.target.value }); - }; - - render() { - const { error, loading, message, recipient, subject, success } = this.state; - return ( - <> - -
-
- {translate('email_configuration.test.title')} -
- -
-
- -
- {success && ( - - {translateWithParameters('email_configuration.test.email_was_sent_to_x', success)} - - )} - - {error !== undefined && {error}} - - - - - - - - - - - - - {translate('email_configuration.test.send')} - - - -
- - ); - } -} - -export default withCurrentUserContext(EmailForm); diff --git a/server/sonar-web/src/main/js/apps/settings/components/SettingsSearch.tsx b/server/sonar-web/src/main/js/apps/settings/components/SettingsSearch.tsx index 9ae187d43b7..8ae5057684a 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/SettingsSearch.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/SettingsSearch.tsx @@ -28,8 +28,8 @@ import { Component, Dict } from '../../../types/types'; import { ADDITIONAL_PROJECT_SETTING_DEFINITIONS, ADDITIONAL_SETTING_DEFINITIONS, - buildSettingLink, -} from '../utils'; +} from '../constants'; +import { buildSettingLink } from '../utils'; import SettingsSearchRenderer from './SettingsSearchRenderer'; interface Props { diff --git a/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx b/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx index cbc93409ded..966415aecbc 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx @@ -25,9 +25,9 @@ import { Location } from '~sonar-aligned/types/router'; import { sanitizeStringRestricted } from '../../../helpers/sanitize'; import { SettingDefinitionAndValue } from '../../../types/settings'; import { Component } from '../../../types/types'; +import { SUB_CATEGORY_EXCLUSIONS } from '../constants'; import { getSubCategoryDescription, getSubCategoryName } from '../utils'; import DefinitionsList from './DefinitionsList'; -import EmailForm from './EmailForm'; export interface SubCategoryDefinitionsListProps { category: string; @@ -42,7 +42,7 @@ export interface SubCategoryDefinitionsListProps { class SubCategoryDefinitionsList extends React.PureComponent { componentDidUpdate(prevProps: SubCategoryDefinitionsListProps) { const { hash } = this.props.location; - if (hash && prevProps.location.hash !== hash) { + if (hash.length > 0 && prevProps.location.hash !== hash) { const query = `[data-scroll-key=${hash.substring(1).replace(/[.#/]/g, '\\$&')}]`; const element = document.querySelector(query); this.scrollToSubCategoryOrDefinition(element); @@ -52,22 +52,15 @@ class SubCategoryDefinitionsList extends React.PureComponent { if (element) { const { hash } = this.props.location; - if (hash && hash.substring(1) === element.getAttribute('data-scroll-key')) { + if (hash.length > 0 && hash.substring(1) === element.getAttribute('data-scroll-key')) { element.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' }); } } }; - renderEmailForm = (subCategoryKey: string) => { - const isEmailSettings = this.props.category === 'general' && subCategoryKey === 'email'; - if (!isEmailSettings) { - return null; - } - return ; - }; - render() { const { + category, displaySubCategoryTitle = true, settings, subCategory, @@ -85,7 +78,8 @@ class SubCategoryDefinitionsList extends React.PureComponent c.key === subCategory) - : sortedSubCategories; + : sortedSubCategories.filter((c) => !SUB_CATEGORY_EXCLUSIONS[category]?.includes(c.key)); + return (
    {filteredSubCategories.map((subCategory, index) => ( @@ -114,8 +108,6 @@ class SubCategoryDefinitionsList extends React.PureComponent - {this.renderEmailForm(subCategory.key)} - { // Add a separator to all but the last element index !== filteredSubCategories.length - 1 && diff --git a/server/sonar-web/src/main/js/apps/settings/components/email-notification/EmailForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/email-notification/EmailForm.tsx new file mode 100644 index 00000000000..4f5c24df21b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/email-notification/EmailForm.tsx @@ -0,0 +1,136 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { Spinner } from '@sonarsource/echoes-react'; +import { + BasicSeparator, + ButtonPrimary, + FlagMessage, + FormField, + InputField, + InputTextArea, + SubHeading, +} from 'design-system'; +import React, { ChangeEvent, FormEvent, useState } from 'react'; +import { useIntl } from 'react-intl'; +import { useCurrentLoginUser } from '../../../../app/components/current-user/CurrentUserContext'; +import MandatoryFieldsExplanation from '../../../../components/ui/MandatoryFieldsExplanation'; +import { translate, translateWithParameters } from '../../../../helpers/l10n'; +import { parseError } from '../../../../helpers/request'; +import { useSendTestEmailMutation } from '../../../../queries/emails'; + +export default function EmailForm() { + const currentUser = useCurrentLoginUser(); + const { formatMessage } = useIntl(); + const { isPending, isSuccess, mutate: sendTestEmail } = useSendTestEmailMutation(); + + const [error, setError] = useState(); + const [message, setMessage] = useState( + formatMessage({ id: 'email_configuration.test.message_text' }), + ); + const [recipient, setRecipient] = useState(currentUser.email ?? ''); + const [subject, setSubject] = useState(formatMessage({ id: 'email_configuration.test.subject' })); + + const handleFormSubmit = (event: FormEvent) => { + event.preventDefault(); + sendTestEmail( + { message, recipient, subject }, + { + onError: async (error) => { + const errorMessage = await parseError(error); + setError(errorMessage); + }, + }, + ); + }; + + const onMessageChange = (event: ChangeEvent) => { + setMessage(event.target.value); + }; + + const onRecipientChange = (event: ChangeEvent) => { + setRecipient(event.target.value); + }; + + const onSubjectChange = (event: ChangeEvent) => { + setSubject(event.target.value); + }; + + return ( + <> + +
    +
    + {translate('email_configuration.test.title')} +
    + +
    +
    + +
    + {isSuccess && ( + + {translateWithParameters('email_configuration.test.email_was_sent_to_x', recipient)} + + )} + + {error !== undefined && {error}} + + + + + + + + + + + + + {translate('email_configuration.test.send')} + + + +
    + + ); +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/email-notification/EmailNotification.tsx b/server/sonar-web/src/main/js/apps/settings/components/email-notification/EmailNotification.tsx new file mode 100644 index 00000000000..b4bd95e965d --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/email-notification/EmailNotification.tsx @@ -0,0 +1,46 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { SubTitle } from 'design-system'; +import React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { AdditionalCategoryComponentProps } from '../AdditionalCategories'; +import CategoryDefinitionsList from '../CategoryDefinitionsList'; +import EmailForm from './EmailForm'; + +export default function EmailNotification(props: Readonly) { + const { component, definitions } = props; + + return ( +
    + + + + + +
    + ); +} diff --git a/server/sonar-web/src/main/js/apps/settings/constants.ts b/server/sonar-web/src/main/js/apps/settings/constants.ts index 3b413065239..4ab46992bbf 100644 --- a/server/sonar-web/src/main/js/apps/settings/constants.ts +++ b/server/sonar-web/src/main/js/apps/settings/constants.ts @@ -17,6 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { AlmKeys } from '../../types/alm-settings'; +import { ExtendedSettingDefinition } from '../../types/settings'; import { Dict } from '../../types/types'; export const ALM_INTEGRATION_CATEGORY = 'almintegration'; @@ -25,6 +27,7 @@ export const ANALYSIS_SCOPE_CATEGORY = 'exclusions'; export const LANGUAGES_CATEGORY = 'languages'; export const NEW_CODE_PERIOD_CATEGORY = 'new_code_period'; export const PULL_REQUEST_DECORATION_BINDING_CATEGORY = 'pull_request_decoration_binding'; +export const EMAIL_NOTIFICATION_CATEGORY = 'email_notification'; export const CATEGORY_OVERRIDES: Dict = { abap: LANGUAGES_CATEGORY, @@ -65,6 +68,144 @@ export const CATEGORY_OVERRIDES: Dict = { yaml: LANGUAGES_CATEGORY, }; +export const SUB_CATEGORY_EXCLUSIONS: Record = { + general: ['email'], +}; + // As per Bitbucket Cloud's documentation, Workspace ID's can only contain lowercase letters, // numbers, dashes, and underscores. export const BITBUCKET_CLOUD_WORKSPACE_ID_FORMAT = /^[a-z0-9\-_]+$/; + +export const ADDITIONAL_PROJECT_SETTING_DEFINITIONS: ExtendedSettingDefinition[] = [ + { + name: 'DevOps Platform Integration', + description: ` + Display your Quality Gate status directly in your DevOps Platform. + Each DevOps Platform instance must be configured globally first, and given a unique name. Pick the instance your project is hosted on. + `, + category: 'pull_request_decoration_binding', + key: ``, + fields: [], + options: [], + subCategory: '', + }, +]; + +export const ADDITIONAL_SETTING_DEFINITIONS: ExtendedSettingDefinition[] = [ + { + name: 'Default New Code behavior', + description: ` + The New Code definition is used to compare measures and track new issues. + This setting is the default for all projects. A specific New Code definition can be configured at project level. + `, + category: 'new_code_period', + key: `sonar.new_code_period`, + fields: [], + options: [], + subCategory: '', + }, + { + name: 'Azure DevOps integration', + description: `azure devops integration configuration + Configuration name + Give your configuration a clear and succinct name. + This name will be used at project level to identify the correct configured Azure instance for a project. + Azure DevOps URL + For Azure DevOps Server, provide the full collection URL: + https://ado.your-company.com/your_collection + + For Azure DevOps Services, provide the full organization URL: + https://dev.azure.com/your_organization + Personal Access Token + SonarQube needs a Personal Access Token to report the Quality Gate status on Pull Requests in Azure DevOps. + To create this token, we recommend using a dedicated Azure DevOps account with administration permissions. + The token itself needs Code > Read & Write permission. + `, + category: 'almintegration', + key: `sonar.almintegration.${AlmKeys.Azure}`, + fields: [], + options: [], + subCategory: '', + }, + { + name: 'Bitbucket integration', + description: `bitbucket server cloud integration configuration + Configuration name + Give your configuration a clear and succinct name. + This name will be used at project level to identify the correct configured Bitbucket instance for a project. + Bitbucket Server URL + Example: https://bitbucket-server.your-company.com + Personal Access Token + SonarQube needs a Personal Access Token to report the Quality Gate status on Pull Requests in Bitbucket Server. + To create this token, we recommend using a dedicated Bitbucket Server account with administration permissions. + The token itself needs Read permission. + Workspace ID + The workspace ID is part of your bitbucket cloud URL https://bitbucket.org/{workspace}/{repository} + SonarQube needs you to create an OAuth consumer in your Bitbucket Cloud workspace settings + to report the Quality Gate status on Pull Requests. + It needs to be a private consumer with Pull Requests: Read permission. + An OAuth callback URL is required by Bitbucket Cloud but not used by SonarQube so any URL works. + OAuth Key + Bitbucket automatically creates an OAuth key when you create your OAuth consumer. + You can find it in your Bitbucket Cloud workspace settings under OAuth consumers. + OAuth Secret + Bitbucket automatically creates an OAuth secret when you create your OAuth consumer. + You can find it in your Bitbucket Cloud workspace settings under OAuth consumers. + `, + category: 'almintegration', + key: `sonar.almintegration.${AlmKeys.BitbucketServer}`, + fields: [], + options: [], + subCategory: '', + }, + { + name: 'GitHub integration', + description: `github integration configuration + Configuration name + Give your configuration a clear and succinct name. + This name will be used at project level to identify the correct configured GitHub App for a project. + GitHub API URL + Example for Github Enterprise: + https://github.company.com/api/v3 + If using GitHub.com: + https://api.github.com/ + You need to install a GitHub App with specific settings and permissions to enable + Pull Request Decoration on your Organization or Repository. + GitHub App ID + The App ID is found on your GitHub App's page on GitHub at Settings > Developer Settings > GitHub Apps + Client ID + The Client ID is found on your GitHub App's page. + Client Secret + The Client secret is found on your GitHub App's page. + Private Key + Your GitHub App's private key. You can generate a .pem file from your GitHub App's page under Private keys. + Copy and paste the whole contents of the file here. + `, + category: 'almintegration', + key: `sonar.almintegration.${AlmKeys.GitHub}`, + fields: [], + options: [], + subCategory: '', + }, + { + name: 'Gitlab integration', + description: `gitlab integration configuration + Configuration name + Give your configuration a clear and succinct name. + This name will be used at project level to identify the correct configured GitLab instance for a project. + GitLab API URL + Provide the GitLab API URL. For example: + https://gitlab.com/api/v4 + Personal Access Token + SonarQube needs a Personal Access Token to report the Quality Gate status on Merge Requests in GitLab. + To create this token, + we recommend using a dedicated GitLab account with Reporter permission to all target projects. + The token itself needs the api scope. + `, + category: 'almintegration', + key: `sonar.almintegration.${AlmKeys.GitLab}`, + fields: [], + options: [], + subCategory: '', + }, +]; diff --git a/server/sonar-web/src/main/js/apps/settings/utils.ts b/server/sonar-web/src/main/js/apps/settings/utils.ts index 81d24b6c2a1..6f76b731c6a 100644 --- a/server/sonar-web/src/main/js/apps/settings/utils.ts +++ b/server/sonar-web/src/main/js/apps/settings/utils.ts @@ -258,137 +258,3 @@ export function buildSettingLink( hash: `#${escape(key)}`, }; } - -export const ADDITIONAL_PROJECT_SETTING_DEFINITIONS: ExtendedSettingDefinition[] = [ - { - name: 'DevOps Platform Integration', - description: ` - Display your Quality Gate status directly in your DevOps Platform. - Each DevOps Platform instance must be configured globally first, and given a unique name. Pick the instance your project is hosted on. - `, - category: 'pull_request_decoration_binding', - key: ``, - fields: [], - options: [], - subCategory: '', - }, -]; - -export const ADDITIONAL_SETTING_DEFINITIONS: ExtendedSettingDefinition[] = [ - { - name: 'Default New Code behavior', - description: ` - The New Code definition is used to compare measures and track new issues. - This setting is the default for all projects. A specific New Code definition can be configured at project level. - `, - category: 'new_code_period', - key: `sonar.new_code_period`, - fields: [], - options: [], - subCategory: '', - }, - { - name: 'Azure DevOps integration', - description: `azure devops integration configuration - Configuration name - Give your configuration a clear and succinct name. - This name will be used at project level to identify the correct configured Azure instance for a project. - Azure DevOps URL - For Azure DevOps Server, provide the full collection URL: - https://ado.your-company.com/your_collection - - For Azure DevOps Services, provide the full organization URL: - https://dev.azure.com/your_organization - Personal Access Token - SonarQube needs a Personal Access Token to report the Quality Gate status on Pull Requests in Azure DevOps. - To create this token, we recommend using a dedicated Azure DevOps account with administration permissions. - The token itself needs Code > Read & Write permission. - `, - category: 'almintegration', - key: `sonar.almintegration.${AlmKeys.Azure}`, - fields: [], - options: [], - subCategory: '', - }, - { - name: 'Bitbucket integration', - description: `bitbucket server cloud integration configuration - Configuration name - Give your configuration a clear and succinct name. - This name will be used at project level to identify the correct configured Bitbucket instance for a project. - Bitbucket Server URL - Example: https://bitbucket-server.your-company.com - Personal Access Token - SonarQube needs a Personal Access Token to report the Quality Gate status on Pull Requests in Bitbucket Server. - To create this token, we recommend using a dedicated Bitbucket Server account with administration permissions. - The token itself needs Read permission. - Workspace ID - The workspace ID is part of your bitbucket cloud URL https://bitbucket.org/{workspace}/{repository} - SonarQube needs you to create an OAuth consumer in your Bitbucket Cloud workspace settings - to report the Quality Gate status on Pull Requests. - It needs to be a private consumer with Pull Requests: Read permission. - An OAuth callback URL is required by Bitbucket Cloud but not used by SonarQube so any URL works. - OAuth Key - Bitbucket automatically creates an OAuth key when you create your OAuth consumer. - You can find it in your Bitbucket Cloud workspace settings under OAuth consumers. - OAuth Secret - Bitbucket automatically creates an OAuth secret when you create your OAuth consumer. - You can find it in your Bitbucket Cloud workspace settings under OAuth consumers. - `, - category: 'almintegration', - key: `sonar.almintegration.${AlmKeys.BitbucketServer}`, - fields: [], - options: [], - subCategory: '', - }, - { - name: 'GitHub integration', - description: `github integration configuration - Configuration name - Give your configuration a clear and succinct name. - This name will be used at project level to identify the correct configured GitHub App for a project. - GitHub API URL - Example for Github Enterprise: - https://github.company.com/api/v3 - If using GitHub.com: - https://api.github.com/ - You need to install a GitHub App with specific settings and permissions to enable - Pull Request Decoration on your Organization or Repository. - GitHub App ID - The App ID is found on your GitHub App's page on GitHub at Settings > Developer Settings > GitHub Apps - Client ID - The Client ID is found on your GitHub App's page. - Client Secret - The Client secret is found on your GitHub App's page. - Private Key - Your GitHub App's private key. You can generate a .pem file from your GitHub App's page under Private keys. - Copy and paste the whole contents of the file here. - `, - category: 'almintegration', - key: `sonar.almintegration.${AlmKeys.GitHub}`, - fields: [], - options: [], - subCategory: '', - }, - { - name: 'Gitlab integration', - description: `gitlab integration configuration - Configuration name - Give your configuration a clear and succinct name. - This name will be used at project level to identify the correct configured GitLab instance for a project. - GitLab API URL - Provide the GitLab API URL. For example: - https://gitlab.com/api/v4 - Personal Access Token - SonarQube needs a Personal Access Token to report the Quality Gate status on Merge Requests in GitLab. - To create this token, - we recommend using a dedicated GitLab account with Reporter permission to all target projects. - The token itself needs the api scope. - `, - category: 'almintegration', - key: `sonar.almintegration.${AlmKeys.GitLab}`, - fields: [], - options: [], - subCategory: '', - }, -]; diff --git a/server/sonar-web/src/main/js/queries/emails.ts b/server/sonar-web/src/main/js/queries/emails.ts new file mode 100644 index 00000000000..091154eb505 --- /dev/null +++ b/server/sonar-web/src/main/js/queries/emails.ts @@ -0,0 +1,36 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { useMutation } from '@tanstack/react-query'; +import { sendTestEmail } from '../api/settings'; + +export function useSendTestEmailMutation() { + return useMutation< + void, + Response, + { + message: string; + recipient: string; + subject: string; + }, + unknown + >({ + mutationFn: ({ message, recipient, subject }) => sendTestEmail(message, recipient, subject), + }); +} diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 1933b50ce59..1d28a55a9ba 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -1734,6 +1734,9 @@ settings.pr_decoration.binding.form.bitbucketcloud.repository.help=The repositor settings.pr_decoration.binding.form.gitlab.repository=Project ID settings.pr_decoration.binding.form.gitlab.repository.help=The Project ID is a numerical unique identifier for your project. You can find it on your Project Overview. +settings.email_notification.category=Email Notification +settings.email_notification.header=SMTP Configuration + property.category.announcement=Announcement property.category.general=General property.category.general.email=Email -- 2.39.5