aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/utils.ts6
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/BranchOverview.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/projectDeletion/Form.tsx67
-rw-r--r--server/sonar-web/src/main/js/queries/applications.ts15
-rw-r--r--server/sonar-web/src/main/js/queries/measures.ts86
-rw-r--r--server/sonar-web/src/main/js/queries/portfolios.ts32
-rw-r--r--server/sonar-web/src/main/js/queries/projects.ts14
-rw-r--r--server/sonar-web/src/main/js/queries/settings.ts2
-rw-r--r--sonar-ws/src/testFixtures/java/org/sonarqube/ws/tester/UserTester.java4
9 files changed, 169 insertions, 58 deletions
diff --git a/server/sonar-web/src/main/js/apps/component-measures/utils.ts b/server/sonar-web/src/main/js/apps/component-measures/utils.ts
index 5e639f5fa11..e8961d51ac7 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/utils.ts
+++ b/server/sonar-web/src/main/js/apps/component-measures/utils.ts
@@ -290,11 +290,7 @@ export function hasFullMeasures(branch?: BranchLike) {
}
export function getMeasuresPageMetricKeys(metrics: Dict<Metric>, branch?: BranchLike) {
- // ToDo rollback once new metrics are available
- const metricKeys = [
- ...getDisplayMetrics(Object.values(metrics)).map((metric) => metric.key),
- ...SOFTWARE_QUALITY_RATING_METRICS,
- ];
+ const metricKeys = getDisplayMetrics(Object.values(metrics)).map((metric) => metric.key);
if (isPullRequest(branch)) {
return metricKeys.filter((key) => isDiffMetric(key));
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/BranchOverview.tsx b/server/sonar-web/src/main/js/apps/overview/branches/BranchOverview.tsx
index abe81a18c4f..b340d736035 100644
--- a/server/sonar-web/src/main/js/apps/overview/branches/BranchOverview.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/BranchOverview.tsx
@@ -126,7 +126,6 @@ export default function BranchOverview(props: Readonly<Props>) {
])
: BRANCH_OVERVIEW_METRICS
: BRANCH_OVERVIEW_METRICS,
- branchParameters: getBranchLikeQuery(branch),
});
const getEnhancedConditions = (
diff --git a/server/sonar-web/src/main/js/apps/projectDeletion/Form.tsx b/server/sonar-web/src/main/js/apps/projectDeletion/Form.tsx
index 9de1ebc5f82..23df77720b9 100644
--- a/server/sonar-web/src/main/js/apps/projectDeletion/Form.tsx
+++ b/server/sonar-web/src/main/js/apps/projectDeletion/Form.tsx
@@ -24,10 +24,11 @@ import * as React from 'react';
import { withRouter } from '~sonar-aligned/components/hoc/withRouter';
import { isPortfolioLike } from '~sonar-aligned/helpers/component';
import { Router } from '~sonar-aligned/types/router';
-import { deleteApplication } from '../../api/application';
-import { deletePortfolio, deleteProject } from '../../api/project-management';
import ConfirmButton from '../../components/controls/ConfirmButton';
import { translate, translateWithParameters } from '../../helpers/l10n';
+import { useDeleteApplicationMutation } from '../../queries/applications';
+import { useDeletePortfolioMutation } from '../../queries/portfolios';
+import { useDeleteProjectMutation } from '../../queries/projects';
import { isApplication } from '../../types/component';
import { Component } from '../../types/types';
@@ -36,9 +37,12 @@ interface Props {
router: Router;
}
-export class Form extends React.PureComponent<Props> {
- handleDelete = async () => {
- const { component } = this.props;
+export function Form({ component, router }: Readonly<Props>) {
+ const { mutate: deleteProject } = useDeleteProjectMutation();
+ const { mutate: deleteApplication } = useDeleteApplicationMutation();
+ const { mutate: deletePortfolio } = useDeletePortfolioMutation();
+
+ const handleDelete = () => {
let deleteMethod = deleteProject;
let redirectTo = '/';
@@ -49,36 +53,35 @@ export class Form extends React.PureComponent<Props> {
deleteMethod = deleteApplication;
}
- await deleteMethod(component.key);
-
- addGlobalSuccessMessage(
- translateWithParameters('project_deletion.resource_deleted', component.name),
- );
+ deleteMethod(component.key, {
+ onSuccess: () => {
+ addGlobalSuccessMessage(
+ translateWithParameters('project_deletion.resource_deleted', component.name),
+ );
- this.props.router.replace(redirectTo);
+ router.replace(redirectTo);
+ },
+ });
};
- render() {
- const { component } = this.props;
- return (
- <ConfirmButton
- confirmButtonText={translate('delete')}
- isDestructive
- modalBody={translateWithParameters(
- 'project_deletion.delete_resource_confirmation',
- component.name,
- )}
- modalHeader={translate('qualifier.delete', component.qualifier)}
- onConfirm={this.handleDelete}
- >
- {({ onClick }) => (
- <Button id="delete-project" onClick={onClick} variety={ButtonVariety.Danger}>
- {translate('delete')}
- </Button>
- )}
- </ConfirmButton>
- );
- }
+ return (
+ <ConfirmButton
+ confirmButtonText={translate('delete')}
+ isDestructive
+ modalBody={translateWithParameters(
+ 'project_deletion.delete_resource_confirmation',
+ component.name,
+ )}
+ modalHeader={translate('qualifier.delete', component.qualifier)}
+ onConfirm={handleDelete}
+ >
+ {({ onClick }) => (
+ <Button id="delete-project" onClick={onClick} variety={ButtonVariety.Danger}>
+ {translate('delete')}
+ </Button>
+ )}
+ </ConfirmButton>
+ );
}
export default withRouter(Form);
diff --git a/server/sonar-web/src/main/js/queries/applications.ts b/server/sonar-web/src/main/js/queries/applications.ts
index 7d3a73afdf2..c08095b30e2 100644
--- a/server/sonar-web/src/main/js/queries/applications.ts
+++ b/server/sonar-web/src/main/js/queries/applications.ts
@@ -18,8 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { useQuery } from '@tanstack/react-query';
-import { getApplicationLeak } from '../api/application';
+import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
+import { deleteApplication, getApplicationLeak } from '../api/application';
+import { invalidateMeasuresByComponentKey } from './measures';
export default function useApplicationLeakQuery(application: string, enabled = true) {
return useQuery({
@@ -28,3 +29,13 @@ export default function useApplicationLeakQuery(application: string, enabled = t
enabled,
});
}
+
+export function useDeleteApplicationMutation() {
+ const queryClient = useQueryClient();
+ return useMutation({
+ mutationFn: (key: string) => deleteApplication(key),
+ onSuccess: (_, key) => {
+ invalidateMeasuresByComponentKey(key, queryClient);
+ },
+ });
+}
diff --git a/server/sonar-web/src/main/js/queries/measures.ts b/server/sonar-web/src/main/js/queries/measures.ts
index 479370883af..dc34baa0637 100644
--- a/server/sonar-web/src/main/js/queries/measures.ts
+++ b/server/sonar-web/src/main/js/queries/measures.ts
@@ -18,7 +18,12 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { infiniteQueryOptions, queryOptions, useQueryClient } from '@tanstack/react-query';
+import {
+ infiniteQueryOptions,
+ QueryClient,
+ queryOptions,
+ useQueryClient,
+} from '@tanstack/react-query';
import { groupBy, isUndefined, omitBy } from 'lodash';
import { BranchParameters } from '~sonar-aligned/types/branch-like';
import { getComponentTree } from '../api/components';
@@ -32,7 +37,29 @@ import { getNextPageParam, getPreviousPageParam } from '../helpers/react-query';
import { getBranchLikeQuery } from '../sonar-aligned/helpers/branch-like';
import { BranchLike } from '../types/branch-like';
import { Measure } from '../types/types';
-import { createInfiniteQueryHook, createQueryHook, StaleTime } from './common';
+import { createInfiniteQueryHook, createQueryHook } from './common';
+
+export const invalidateMeasuresByComponentKey = (
+ componentKey: string,
+ queryClient: QueryClient,
+) => {
+ queryClient.invalidateQueries({ queryKey: ['measures', 'history', componentKey] });
+ queryClient.invalidateQueries({ queryKey: ['measures', 'component', componentKey] });
+ queryClient.invalidateQueries({ queryKey: ['measures', 'details', componentKey] });
+ queryClient.invalidateQueries({ queryKey: ['measures', 'list', componentKey] });
+ queryClient.invalidateQueries({
+ predicate: (query) =>
+ query.queryKey[0] === 'measures' &&
+ query.queryKey[1] === 'list' &&
+ query.queryKey[2] === 'projects' &&
+ Array.isArray(query.queryKey[3]) &&
+ query.queryKey[3].includes(componentKey),
+ });
+};
+
+export const invalidateAllMeasures = (queryClient: QueryClient) => {
+ queryClient.invalidateQueries({ queryKey: ['measures'] });
+};
export const useAllMeasuresHistoryQuery = createQueryHook(
({
@@ -71,7 +98,14 @@ export const useMeasuresComponentQuery = createQueryHook(
const branchLikeQuery = getBranchLikeQuery(branchLike);
return queryOptions({
- queryKey: ['measures', 'component', componentKey, 'branchLike', branchLikeQuery, metricKeys],
+ queryKey: [
+ 'measures',
+ 'component',
+ componentKey,
+ 'branchLike',
+ { ...branchLikeQuery },
+ metricKeys,
+ ],
queryFn: async () => {
const data = await getMeasuresWithPeriodAndMetrics(
componentKey,
@@ -82,7 +116,7 @@ export const useMeasuresComponentQuery = createQueryHook(
const measure =
data.component.measures?.find((measure) => measure.metric === metricKey) ?? null;
queryClient.setQueryData<Measure | null>(
- ['measures', 'details', componentKey, 'branchLike', branchLikeQuery, metricKey],
+ ['measures', 'details', componentKey, 'branchLike', { ...branchLikeQuery }, metricKey],
measure,
);
});
@@ -115,7 +149,7 @@ export const useComponentTreeQuery = createInfiniteQueryHook(
const queryClient = useQueryClient();
return infiniteQueryOptions({
- queryKey: ['component', component, 'tree', strategy, { metrics, additionalData }],
+ queryKey: ['measures', 'component', component, 'tree', strategy, { metrics, additionalData }],
queryFn: async ({ pageParam }) => {
const result = await getComponentTree(strategy, component, metrics, {
...additionalData,
@@ -136,7 +170,7 @@ export const useComponentTreeQuery = createInfiniteQueryHook(
'details',
result.baseComponent.key,
'branchLike',
- branchLikeQuery,
+ { ...branchLikeQuery },
metricKey,
],
measure,
@@ -153,7 +187,14 @@ export const useComponentTreeQuery = createInfiniteQueryHook(
metrics?.forEach((metricKey) => {
const measure = measuresMapByMetricKeyForChildComponent[metricKey]?.[0] ?? null;
queryClient.setQueryData<Measure>(
- ['measures', 'details', childComponent.key, 'branchLike', branchLikeQuery, metricKey],
+ [
+ 'measures',
+ 'details',
+ childComponent.key,
+ 'branchLike',
+ { ...branchLikeQuery },
+ metricKey,
+ ],
measure,
);
});
@@ -199,23 +240,21 @@ export const useMeasuresAndLeakQuery = createQueryHook(
componentKey,
metricKeys,
branchLike,
- branchParameters,
}: {
branchLike?: BranchLike;
- branchParameters?: BranchParameters;
componentKey: string;
metricKeys: string[];
}) => {
const queryClient = useQueryClient();
+ const branchParameters = getBranchLikeQuery(branchLike);
return queryOptions({
queryKey: [
'measures',
'details',
- 'component',
componentKey,
+ 'branchLike',
+ { ...branchParameters },
metricKeys,
- branchLike,
- branchParameters,
],
queryFn: async () => {
const { component, metrics, period } = await getMeasuresWithPeriodAndMetrics(
@@ -227,7 +266,7 @@ export const useMeasuresAndLeakQuery = createQueryHook(
metricKeys.forEach((metricKey) => {
const measure = measuresMapByMetricKey[metricKey]?.[0] ?? null;
queryClient.setQueryData<Measure>(
- ['measures', 'details', componentKey, 'branchLike', branchLike, metricKey],
+ ['measures', 'details', componentKey, 'branchLike', { ...branchParameters }, metricKey],
measure,
);
});
@@ -250,7 +289,14 @@ export const useMeasureQuery = createQueryHook(
const branchLikeQuery = getBranchLikeQuery(branchLike);
return queryOptions({
- queryKey: ['measures', 'details', componentKey, 'branchLike', branchLikeQuery, metricKey],
+ queryKey: [
+ 'measures',
+ 'details',
+ componentKey,
+ 'branchLike',
+ { ...branchLikeQuery },
+ metricKey,
+ ],
queryFn: () =>
getMeasures({ component: componentKey, metricKeys: metricKey }).then(
(measures) => measures[0] ?? null,
@@ -274,7 +320,14 @@ export const useMeasuresQuery = createQueryHook(
const branchLikeQuery = getBranchLikeQuery(branchLike);
return queryOptions({
- queryKey: ['measures', 'list', componentKey, 'branchLike', branchLikeQuery, metricKeys],
+ queryKey: [
+ 'measures',
+ 'list',
+ componentKey,
+ 'branchLike',
+ { ...branchLikeQuery },
+ metricKeys,
+ ],
queryFn: async () => {
const measures = await getMeasures({
component: componentKey,
@@ -285,13 +338,12 @@ export const useMeasuresQuery = createQueryHook(
metricKeys.split(',').forEach((metricKey) => {
const measure = measuresMapByMetricKey[metricKey]?.[0] ?? null;
queryClient.setQueryData<Measure>(
- ['measures', 'details', componentKey, 'branchLike', branchLike ?? {}, metricKey],
+ ['measures', 'details', componentKey, 'branchLike', { ...branchLikeQuery }, metricKey],
measure,
);
});
return measures;
},
- staleTime: StaleTime.LONG,
});
},
);
diff --git a/server/sonar-web/src/main/js/queries/portfolios.ts b/server/sonar-web/src/main/js/queries/portfolios.ts
new file mode 100644
index 00000000000..c8e4182011b
--- /dev/null
+++ b/server/sonar-web/src/main/js/queries/portfolios.ts
@@ -0,0 +1,32 @@
+/*
+ * 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, useQueryClient } from '@tanstack/react-query';
+import { deletePortfolio } from '../api/project-management';
+import { invalidateMeasuresByComponentKey } from './measures';
+
+export function useDeletePortfolioMutation() {
+ const queryClient = useQueryClient();
+ return useMutation({
+ mutationFn: (key: string) => deletePortfolio(key),
+ onSuccess: (_, key) => {
+ invalidateMeasuresByComponentKey(key, queryClient);
+ },
+ });
+}
diff --git a/server/sonar-web/src/main/js/queries/projects.ts b/server/sonar-web/src/main/js/queries/projects.ts
index 39eab402621..38921f593ea 100644
--- a/server/sonar-web/src/main/js/queries/projects.ts
+++ b/server/sonar-web/src/main/js/queries/projects.ts
@@ -17,9 +17,11 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { queryOptions } from '@tanstack/react-query';
+import { queryOptions, useMutation, useQueryClient } from '@tanstack/react-query';
import { searchProjects } from '../api/components';
+import { deleteProject } from '../api/project-management';
import { createQueryHook } from './common';
+import { invalidateMeasuresByComponentKey } from './measures';
export const useProjectQuery = createQueryHook((key: string) => {
return queryOptions({
@@ -27,3 +29,13 @@ export const useProjectQuery = createQueryHook((key: string) => {
queryFn: ({ queryKey: [, key] }) => searchProjects({ filter: `query=${key}` }),
});
});
+
+export function useDeleteProjectMutation() {
+ const queryClient = useQueryClient();
+ return useMutation({
+ mutationFn: (key: string) => deleteProject(key),
+ onSuccess: (_, key) => {
+ invalidateMeasuresByComponentKey(key, queryClient);
+ },
+ });
+}
diff --git a/server/sonar-web/src/main/js/queries/settings.ts b/server/sonar-web/src/main/js/queries/settings.ts
index 2648e452844..5deba9ca198 100644
--- a/server/sonar-web/src/main/js/queries/settings.ts
+++ b/server/sonar-web/src/main/js/queries/settings.ts
@@ -23,6 +23,7 @@ import { getValue, getValues, resetSettingValue, setSettingValue } from '../api/
import { translate } from '../helpers/l10n';
import { ExtendedSettingDefinition, SettingsKey } from '../types/settings';
import { createQueryHook } from './common';
+import { invalidateAllMeasures } from './measures';
type SettingValue = string | boolean | string[];
@@ -125,6 +126,7 @@ export function useSaveValueMutation() {
onSuccess: (_, { definition }) => {
queryClient.invalidateQueries({ queryKey: ['settings', 'details', definition.key] });
queryClient.invalidateQueries({ queryKey: ['settings', 'values'] });
+ invalidateAllMeasures(queryClient);
addGlobalSuccessMessage(translate('settings.authentication.form.settings.save_success'));
},
});
diff --git a/sonar-ws/src/testFixtures/java/org/sonarqube/ws/tester/UserTester.java b/sonar-ws/src/testFixtures/java/org/sonarqube/ws/tester/UserTester.java
index a376b110811..350cb661a1a 100644
--- a/sonar-ws/src/testFixtures/java/org/sonarqube/ws/tester/UserTester.java
+++ b/sonar-ws/src/testFixtures/java/org/sonarqube/ws/tester/UserTester.java
@@ -122,6 +122,10 @@ public class UserTester {
new org.sonarqube.ws.client.permissions.AddUserRequest()
.setLogin(u.getLogin())
.setPermission("portfoliocreator"));
+ session.wsClient().permissions().addUser(
+ new org.sonarqube.ws.client.permissions.AddUserRequest()
+ .setLogin(u.getLogin())
+ .setPermission("admin"));
return u;
}