aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorMathieu Suen <mathieu.suen@sonarsource.com>2023-05-16 09:59:41 +0200
committersonartech <sonartech@sonarsource.com>2023-05-22 20:02:56 +0000
commitba4f89b285cf1cc1d3700ce8955c52a71bebcb17 (patch)
treeed78c80da53fbcbf30bed9003a0974568f78240c /server/sonar-web
parent3217fa45d0694e7769ebb435bb1bfb554b0b5274 (diff)
downloadsonarqube-ba4f89b285cf1cc1d3700ce8955c52a71bebcb17.tar.gz
sonarqube-ba4f89b285cf1cc1d3700ce8955c52a71bebcb17.zip
[NO_JIRA] Use react-query to get the identity provider
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/package.json1
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/SettingsApp.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx17
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/authentication/GithubAuthenticationTab.tsx10
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/authentication/SamlAuthenticationTab.tsx25
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/authentication/hook/useGithubConfiguration.ts47
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/authentication/hook/useSamlConfiguration.ts20
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/authentication/queries/IdentityProvider.ts86
-rw-r--r--server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx22
-rw-r--r--server/sonar-web/yarn.lock36
10 files changed, 177 insertions, 96 deletions
diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json
index c9dd15014ff..0ffafc16ab7 100644
--- a/server/sonar-web/package.json
+++ b/server/sonar-web/package.json
@@ -11,6 +11,7 @@
"@emotion/react": "11.10.6",
"@emotion/styled": "11.10.6",
"@primer/octicons-react": "18.3.0",
+ "@tanstack/react-query": "4.29.7",
"classnames": "2.3.2",
"clipboard": "2.0.11",
"core-js": "3.29.1",
diff --git a/server/sonar-web/src/main/js/apps/settings/components/SettingsApp.tsx b/server/sonar-web/src/main/js/apps/settings/components/SettingsApp.tsx
index e05ce398856..f10b4a70348 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/SettingsApp.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/SettingsApp.tsx
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import * as React from 'react';
import { getDefinitions } from '../../../api/settings';
import withComponentContext from '../../../app/components/componentContext/withComponentContext';
@@ -31,6 +32,8 @@ import { Component } from '../../../types/types';
import '../styles.css';
import SettingsAppRenderer from './SettingsAppRenderer';
+const queryClient = new QueryClient();
+
interface Props {
component?: Component;
}
@@ -77,7 +80,11 @@ class SettingsApp extends React.PureComponent<Props, State> {
render() {
const { component } = this.props;
- return <SettingsAppRenderer component={component} {...this.state} />;
+ return (
+ <QueryClientProvider client={queryClient}>
+ <SettingsAppRenderer component={component} {...this.state} />
+ </QueryClientProvider>
+ );
}
}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx
index 96d3160caef..7d9b411e7ae 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx
@@ -19,10 +19,8 @@
*/
import classNames from 'classnames';
import * as React from 'react';
-import { useCallback, useEffect, useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { useSearchParams } from 'react-router-dom';
-import { getSystemInfo } from '../../../../api/system';
import withAvailableFeatures, {
WithAvailableFeaturesProps,
} from '../../../../app/components/available-features/withAvailableFeatures';
@@ -37,7 +35,6 @@ import { searchParamsToQuery } from '../../../../helpers/urls';
import { AlmKeys } from '../../../../types/alm-settings';
import { Feature } from '../../../../types/features';
import { ExtendedSettingDefinition } from '../../../../types/settings';
-import { SysInfoCluster } from '../../../../types/types';
import { AUTHENTICATION_CATEGORY } from '../../constants';
import CategoryDefinitionsList from '../CategoryDefinitionsList';
import GithubAuthenticationTab from './GithubAuthenticationTab';
@@ -78,16 +75,6 @@ export function Authentication(props: Props & WithAvailableFeaturesProps) {
const { definitions } = props;
const [query, setSearchParams] = useSearchParams();
- const [provider, setProvider] = useState<string>();
-
- const loadProvider = useCallback(async () => {
- const info = (await getSystemInfo()) as SysInfoCluster;
- setProvider(info.System['External Users and Groups Provisioning']);
- }, []);
-
- useEffect(() => {
- loadProvider();
- }, []);
const currentTab = (query.get('tab') || SAML) as AuthenticationTabs;
@@ -182,16 +169,12 @@ export function Authentication(props: Props & WithAvailableFeaturesProps) {
{tab.key === SAML && (
<SamlAuthenticationTab
definitions={definitions.filter((def) => def.subCategory === SAML)}
- provider={provider}
- onReload={() => loadProvider()}
/>
)}
{tab.key === AlmKeys.GitHub && (
<GithubAuthenticationTab
definitions={definitions.filter((def) => def.subCategory === AlmKeys.GitHub)}
- provider={provider}
- onReload={() => loadProvider()}
/>
)}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/GithubAuthenticationTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/GithubAuthenticationTab.tsx
index ca32c00991b..44f704a755b 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/authentication/GithubAuthenticationTab.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/GithubAuthenticationTab.tsx
@@ -36,11 +36,10 @@ import { DOCUMENTATION_LINK_SUFFIXES } from './Authentication';
import AuthenticationFormField from './AuthenticationFormField';
import ConfigurationForm from './ConfigurationForm';
import useGithubConfiguration, { GITHUB_JIT_FIELDS } from './hook/useGithubConfiguration';
+import { useIdentityProvierQuery } from './queries/IdentityProvider';
interface GithubAuthenticationProps {
definitions: ExtendedSettingDefinition[];
- provider: string | undefined;
- onReload: () => void;
}
const GITHUB_EXCLUDED_FIELD = [
@@ -51,7 +50,8 @@ const GITHUB_EXCLUDED_FIELD = [
];
export default function GithubAuthenticationTab(props: GithubAuthenticationProps) {
- const { definitions, provider } = props;
+ const { definitions } = props;
+ const { data } = useIdentityProvierQuery();
const [showEditModal, setShowEditModal] = useState(false);
const [showConfirmProvisioningModal, setShowConfirmProvisioningModal] = useState(false);
@@ -76,9 +76,9 @@ export default function GithubAuthenticationTab(props: GithubAuthenticationProps
changeProvisioning,
toggleEnable,
hasLegacyConfiguration,
- } = useGithubConfiguration(definitions, props.onReload);
+ } = useGithubConfiguration(definitions);
- const hasDifferentProvider = provider !== undefined && provider !== Provider.Github;
+ const hasDifferentProvider = data?.provider !== undefined && data.provider !== Provider.Github;
const handleCreateConfiguration = () => {
setShowEditModal(true);
diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/SamlAuthenticationTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/SamlAuthenticationTab.tsx
index 44722f68f37..092ae89ede2 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/authentication/SamlAuthenticationTab.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/SamlAuthenticationTab.tsx
@@ -20,12 +20,7 @@
import { isEmpty } from 'lodash';
import React from 'react';
import { FormattedMessage } from 'react-intl';
-import {
- activateScim,
- deactivateScim,
- resetSettingValue,
- setSettingValue,
-} from '../../../../api/settings';
+import { resetSettingValue, setSettingValue } from '../../../../api/settings';
import DocLink from '../../../../components/common/DocLink';
import Link from '../../../../components/common/Link';
import ConfirmModal from '../../../../components/controls/ConfirmModal';
@@ -47,11 +42,10 @@ import useSamlConfiguration, {
SAML_GROUP_NAME,
SAML_SCIM_DEPRECATED,
} from './hook/useSamlConfiguration';
+import { useIdentityProvierQuery, useToggleScimMutation } from './queries/IdentityProvider';
interface SamlAuthenticationProps {
definitions: ExtendedSettingDefinition[];
- provider: string | undefined;
- onReload: () => void;
}
export const SAML = 'saml';
@@ -60,7 +54,7 @@ const CONFIG_TEST_PATH = '/saml/validation_init';
const SAML_EXCLUDED_FIELD = [SAML_ENABLED_FIELD, SAML_GROUP_NAME, SAML_SCIM_DEPRECATED];
export default function SamlAuthenticationTab(props: SamlAuthenticationProps) {
- const { definitions, provider, onReload } = props;
+ const { definitions } = props;
const [showEditModal, setShowEditModal] = React.useState(false);
const [showConfirmProvisioningModal, setShowConfirmProvisioningModal] = React.useState(false);
const {
@@ -81,9 +75,12 @@ export default function SamlAuthenticationTab(props: SamlAuthenticationProps) {
setNewGroupSetting,
reload,
deleteConfiguration,
- } = useSamlConfiguration(definitions, onReload);
+ } = useSamlConfiguration(definitions);
+ const toggleScim = useToggleScimMutation();
- const hasDifferentProvider = provider !== undefined && provider !== Provider.Scim;
+ const { data } = useIdentityProvierQuery();
+
+ const hasDifferentProvider = data?.provider !== undefined && data.provider !== Provider.Scim;
const handleCreateConfiguration = () => {
setShowEditModal(true);
@@ -111,10 +108,8 @@ export default function SamlAuthenticationTab(props: SamlAuthenticationProps) {
};
const handleConfirmChangeProvisioning = async () => {
- if (newScimStatus) {
- await activateScim();
- } else {
- await deactivateScim();
+ await toggleScim.mutateAsync(!!newScimStatus);
+ if (!newScimStatus) {
await handleSaveGroup();
}
await reload();
diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/hook/useGithubConfiguration.ts b/server/sonar-web/src/main/js/apps/settings/components/authentication/hook/useGithubConfiguration.ts
index dea619fd54a..8c9c255467b 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/authentication/hook/useGithubConfiguration.ts
+++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/hook/useGithubConfiguration.ts
@@ -18,17 +18,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { isEmpty, some } from 'lodash';
-import { useCallback, useContext, useEffect, useState } from 'react';
-import {
- activateGithubProvisioning,
- deactivateGithubProvisioning,
- fetchIsGithubProvisioningEnabled,
- resetSettingValue,
- setSettingValue,
-} from '../../../../../api/settings';
+import { useCallback, useContext, useState } from 'react';
+import { resetSettingValue, setSettingValue } from '../../../../../api/settings';
import { AvailableFeaturesContext } from '../../../../../app/components/available-features/AvailableFeaturesContext';
import { Feature } from '../../../../../types/features';
import { ExtendedSettingDefinition } from '../../../../../types/settings';
+import {
+ useGithubStatusQuery,
+ useToggleGithubProvisioningMutation,
+} from '../queries/IdentityProvider';
import useConfiguration from './useConfiguration';
export const GITHUB_ENABLED_FIELD = 'sonar.auth.github.enabled';
@@ -52,16 +50,15 @@ export interface SamlSettingValue {
definition: ExtendedSettingDefinition;
}
-export default function useGithubConfiguration(
- definitions: ExtendedSettingDefinition[],
- onReload: () => void
-) {
+export default function useGithubConfiguration(definitions: ExtendedSettingDefinition[]) {
const config = useConfiguration(definitions, OPTIONAL_FIELDS);
const { values, isValueChange, setNewValue, reload: reloadConfig } = config;
+
const hasGithubProvisioning = useContext(AvailableFeaturesContext).includes(
Feature.GithubProvisioning
);
- const [githubProvisioningStatus, setGithubProvisioningStatus] = useState(false);
+ const { data: githubProvisioningStatus } = useGithubStatusQuery();
+ const toggleGithubProvisioning = useToggleGithubProvisioningMutation();
const [newGithubProvisioningStatus, setNewGithubProvisioningStatus] = useState<boolean>();
const hasGithubProvisioningConfigChange =
some(GITHUB_JIT_FIELDS, isValueChange) ||
@@ -72,14 +69,6 @@ export default function useGithubConfiguration(
GITHUB_JIT_FIELDS.forEach((s) => setNewValue(s));
};
- useEffect(() => {
- (async () => {
- if (hasGithubProvisioning) {
- setGithubProvisioningStatus(await fetchIsGithubProvisioningEnabled());
- }
- })();
- }, [hasGithubProvisioning]);
-
const enabled = values[GITHUB_ENABLED_FIELD]?.value === 'true';
const appId = values[GITHUB_APP_ID_FIELD]?.value as string;
const url = values[GITHUB_API_URL_FIELD]?.value;
@@ -87,20 +76,13 @@ export default function useGithubConfiguration(
const reload = useCallback(async () => {
await reloadConfig();
- setGithubProvisioningStatus(await fetchIsGithubProvisioningEnabled());
- onReload();
- }, [reloadConfig, onReload]);
+ }, [reloadConfig]);
const changeProvisioning = async () => {
- if (newGithubProvisioningStatus && newGithubProvisioningStatus !== githubProvisioningStatus) {
- await activateGithubProvisioning();
- await reload();
- } else {
- if (newGithubProvisioningStatus !== githubProvisioningStatus) {
- await deactivateGithubProvisioning();
- }
- await saveGroup();
+ if (newGithubProvisioningStatus !== githubProvisioningStatus) {
+ await toggleGithubProvisioning.mutateAsync(!!newGithubProvisioningStatus);
}
+ await saveGroup();
};
const saveGroup = async () => {
@@ -134,7 +116,6 @@ export default function useGithubConfiguration(
enabled,
appId,
hasGithubProvisioning,
- setGithubProvisioningStatus,
githubProvisioningStatus,
newGithubProvisioningStatus,
setNewGithubProvisioningStatus,
diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/hook/useSamlConfiguration.ts b/server/sonar-web/src/main/js/apps/settings/components/authentication/hook/useSamlConfiguration.ts
index 034c8ee5cfa..aed3fa84031 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/authentication/hook/useSamlConfiguration.ts
+++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/hook/useSamlConfiguration.ts
@@ -18,10 +18,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
-import { fetchIsScimEnabled } from '../../../../../api/settings';
import { AvailableFeaturesContext } from '../../../../../app/components/available-features/AvailableFeaturesContext';
import { Feature } from '../../../../../types/features';
import { ExtendedSettingDefinition } from '../../../../../types/settings';
+import { useScimStatusQuery } from '../queries/IdentityProvider';
import useConfiguration from './useConfiguration';
export const SAML_ENABLED_FIELD = 'sonar.auth.saml.enabled';
@@ -39,23 +39,13 @@ const OPTIONAL_FIELDS = [
SAML_SCIM_DEPRECATED,
];
-export default function useSamlConfiguration(
- definitions: ExtendedSettingDefinition[],
- onReload: () => void
-) {
- const [scimStatus, setScimStatus] = React.useState<boolean>(false);
+export default function useSamlConfiguration(definitions: ExtendedSettingDefinition[]) {
const [newScimStatus, setNewScimStatus] = React.useState<boolean>();
const hasScim = React.useContext(AvailableFeaturesContext).includes(Feature.Scim);
const config = useConfiguration(definitions, OPTIONAL_FIELDS);
const { reload: reloadConfig, values, setNewValue, isValueChange } = config;
- React.useEffect(() => {
- (async () => {
- if (hasScim) {
- setScimStatus(await fetchIsScimEnabled());
- }
- })();
- }, [hasScim]);
+ const { data: scimStatus } = useScimStatusQuery();
const name = values[SAML_PROVIDER_NAME]?.value;
const url = values[SAML_LOGIN_URL]?.value;
@@ -71,9 +61,7 @@ export default function useSamlConfiguration(
const reload = React.useCallback(async () => {
await reloadConfig();
- setScimStatus(await fetchIsScimEnabled());
- onReload();
- }, [reloadConfig, onReload]);
+ }, [reloadConfig]);
return {
...config,
diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/queries/IdentityProvider.ts b/server/sonar-web/src/main/js/apps/settings/components/authentication/queries/IdentityProvider.ts
new file mode 100644
index 00000000000..9a0687c630f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/queries/IdentityProvider.ts
@@ -0,0 +1,86 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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, useQuery, useQueryClient } from '@tanstack/react-query';
+import { useContext } from 'react';
+import {
+ activateGithubProvisioning,
+ activateScim,
+ deactivateGithubProvisioning,
+ deactivateScim,
+ fetchIsGithubProvisioningEnabled,
+ fetchIsScimEnabled,
+} from '../../../../../api/settings';
+import { getSystemInfo } from '../../../../../api/system';
+import { AvailableFeaturesContext } from '../../../../../app/components/available-features/AvailableFeaturesContext';
+import { Feature } from '../../../../../types/features';
+import { SysInfoCluster } from '../../../../../types/types';
+
+export function useIdentityProvierQuery() {
+ return useQuery(['identity_provider'], async () => {
+ const info = (await getSystemInfo()) as SysInfoCluster;
+ return { provider: info.System['External Users and Groups Provisioning'] };
+ });
+}
+
+export function useScimStatusQuery() {
+ const hasScim = useContext(AvailableFeaturesContext).includes(Feature.Scim);
+
+ return useQuery(['identity_provider', 'scim_status'], () => {
+ if (!hasScim) {
+ return false;
+ }
+ return fetchIsScimEnabled();
+ });
+}
+
+export function useGithubStatusQuery() {
+ const hasGithubProvisioning = useContext(AvailableFeaturesContext).includes(
+ Feature.GithubProvisioning
+ );
+
+ return useQuery(['identity_provider', 'github_status'], () => {
+ if (!hasGithubProvisioning) {
+ return false;
+ }
+ return fetchIsGithubProvisioningEnabled();
+ });
+}
+
+export function useToggleScimMutation() {
+ const client = useQueryClient();
+ return useMutation({
+ mutationFn: (activate: boolean) => (activate ? activateScim() : deactivateScim()),
+ onSuccess: () => {
+ client.invalidateQueries({ queryKey: ['identity_provider'] });
+ },
+ });
+}
+
+export function useToggleGithubProvisioningMutation() {
+ const client = useQueryClient();
+ return useMutation({
+ mutationFn: (activate: boolean) =>
+ activate ? activateGithubProvisioning() : deactivateGithubProvisioning(),
+ onSuccess: () => {
+ client.invalidateQueries({ queryKey: ['identity_provider'] });
+ },
+ });
+}
diff --git a/server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx b/server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx
index 1d0ffcf5f5c..765b22ef21c 100644
--- a/server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx
+++ b/server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { fireEvent, Matcher, render, RenderResult, screen, within } from '@testing-library/react';
import { UserEvent } from '@testing-library/user-event/dist/types/setup/setup';
import { omit } from 'lodash';
@@ -43,6 +44,7 @@ import { mockComponent } from './mocks/component';
import { DEFAULT_METRICS } from './mocks/metrics';
import { mockAppState, mockCurrentUser } from './testMocks';
+const queryClient = new QueryClient();
export interface RenderContext {
metrics?: Dict<Metric>;
appState?: AppState;
@@ -98,15 +100,17 @@ export function renderComponent(
function Wrapper({ children }: { children: React.ReactElement }) {
return (
<IntlProvider defaultLocale="en" locale="en">
- <HelmetProvider>
- <AppStateContextProvider appState={appState}>
- <MemoryRouter initialEntries={[pathname]}>
- <Routes>
- <Route path="*" element={children} />
- </Routes>
- </MemoryRouter>
- </AppStateContextProvider>
- </HelmetProvider>
+ <QueryClientProvider client={queryClient}>
+ <HelmetProvider>
+ <AppStateContextProvider appState={appState}>
+ <MemoryRouter initialEntries={[pathname]}>
+ <Routes>
+ <Route path="*" element={children} />
+ </Routes>
+ </MemoryRouter>
+ </AppStateContextProvider>
+ </HelmetProvider>
+ </QueryClientProvider>
</IntlProvider>
);
}
diff --git a/server/sonar-web/yarn.lock b/server/sonar-web/yarn.lock
index 90e4d51539d..5e21aa42090 100644
--- a/server/sonar-web/yarn.lock
+++ b/server/sonar-web/yarn.lock
@@ -3565,6 +3565,32 @@ __metadata:
languageName: node
linkType: hard
+"@tanstack/query-core@npm:4.29.7":
+ version: 4.29.7
+ resolution: "@tanstack/query-core@npm:4.29.7"
+ checksum: f28441a1fc17881d770ebacbe3a2fcf58bd065ac0cf58f8dc544d367f00d7f65213e09a1a1280e021eef25a50b307cac6884173b5acf32570e755baf29b741aa
+ languageName: node
+ linkType: hard
+
+"@tanstack/react-query@npm:4.29.7":
+ version: 4.29.7
+ resolution: "@tanstack/react-query@npm:4.29.7"
+ dependencies:
+ "@tanstack/query-core": 4.29.7
+ use-sync-external-store: ^1.2.0
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+ react-native: "*"
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+ react-native:
+ optional: true
+ checksum: 9db2fb78e18f299c0e58f3c177261f6010fdea63a5f950e4c93a7cf7a46da019aa3171c77759fa7b327e35763e3379f0a885c71dafd9aa99bf2ddda9f264de90
+ languageName: node
+ linkType: hard
+
"@testing-library/dom@npm:8.20.0":
version: 8.20.0
resolution: "@testing-library/dom@npm:8.20.0"
@@ -4507,6 +4533,7 @@ __metadata:
"@primer/octicons-react": 18.3.0
"@swc/core": 1.3.44
"@swc/jest": 0.2.24
+ "@tanstack/react-query": 4.29.7
"@testing-library/dom": 8.20.0
"@testing-library/jest-dom": 5.16.5
"@testing-library/react": 12.1.5
@@ -12574,6 +12601,15 @@ __metadata:
languageName: node
linkType: hard
+"use-sync-external-store@npm:^1.2.0":
+ version: 1.2.0
+ resolution: "use-sync-external-store@npm:1.2.0"
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ checksum: 5c639e0f8da3521d605f59ce5be9e094ca772bd44a4ce7322b055a6f58eeed8dda3c94cabd90c7a41fb6fa852210092008afe48f7038792fd47501f33299116a
+ languageName: node
+ linkType: hard
+
"util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1":
version: 1.0.2
resolution: "util-deprecate@npm:1.0.2"