From 09542b3ad8b39a8db4a28c3e81489a42950b2980 Mon Sep 17 00:00:00 2001 From: Guillaume Grossetie Date: Tue, 4 Feb 2025 17:52:03 +0100 Subject: [PATCH] =?UTF-8?q?wip:=20d=C3=A9but=20de=20suppression=20de=20use?= =?UTF-8?q?Mutate,=20runMutation=20et=20useMutation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/components/ArticleContributors.jsx | 39 ++--------- front/src/helpers/graphQL.js | 53 ++++++++++----- front/src/hooks/contributor.js | 45 +++++++++++++ front/src/hooks/graphql.js | 69 ++++++-------------- front/src/hooks/workspace.js | 42 ++++++------ 5 files changed, 127 insertions(+), 121 deletions(-) create mode 100644 front/src/hooks/contributor.js diff --git a/front/src/components/ArticleContributors.jsx b/front/src/components/ArticleContributors.jsx index ec49b55e5..fefe9acbc 100644 --- a/front/src/components/ArticleContributors.jsx +++ b/front/src/components/ArticleContributors.jsx @@ -1,48 +1,32 @@ import React, { useCallback } from 'react' +import { useArticleContributorActions } from '../hooks/contributor.js' import { useMutate, useMutation } from '../hooks/graphql.js' import styles from './articleContributors.module.scss' import ContactSearch from './ContactSearch.jsx' -import { - addContributor, - removeContributor, -} from './ArticleContributors.graphql' + import { getArticleContributors } from './Article.graphql' import { useToasts } from '@geist-ui/core' export default function ArticleContributors({ article, contributors }) { - const mutation = useMutation() const { setToast } = useToasts() const articleId = article._id - const { mutate } = useMutate({ - query: getArticleContributors, - variables: { articleId }, + const { addContributor, removeContributor } = useArticleContributorActions({ + articleId, }) - const handleUserUpdated = useCallback( async ({ user, action }) => { const { _id: userId } = user if (action === 'select') { // add contributor try { - const response = await mutation({ - query: addContributor, - variables: { userId, articleId }, - }) + await addContributor(userId) setToast({ text: `Contributeur ${ user.displayName || user.username } ajouté à l'article.`, type: 'default', }) - await mutate( - { - article: { - contributors: response.article.addContributor.contributors, - }, - }, - { revalidate: false } - ) } catch (err) { setToast({ text: String(err), @@ -51,24 +35,13 @@ export default function ArticleContributors({ article, contributors }) { } } else if (action === 'unselect') { try { - const response = await mutation({ - query: removeContributor, - variables: { userId, articleId }, - }) + await removeContributor(userId) setToast({ text: `Contributeur ${ user.displayName || user.username } supprimé de l'article.`, type: 'warning', }) - await mutate( - { - article: { - contributors: response.article.removeContributor.contributors, - }, - }, - { revalidate: false } - ) } catch (err) { setToast({ text: String(err), diff --git a/front/src/helpers/graphQL.js b/front/src/helpers/graphQL.js index 0c908df13..9f37c9efa 100644 --- a/front/src/helpers/graphQL.js +++ b/front/src/helpers/graphQL.js @@ -22,12 +22,18 @@ async function getErrorResponse(response) { } /** - * @param {string} sessionToken - * @param {string} query - * @param {{[string: key]: value}|undefined} variables + * @param {string} query request query (as string) + * @param {{[string: key]: value}|undefined} variables request variables + * @param {string} sessionToken session token (for authentication) + * @param {'fetch'|'mutate'} [type='fetch'] request type (either fetch or mutate) * @returns {Promise} */ -async function executeRequest({ sessionToken, query, variables }) { +async function executeRequest({ + query, + variables, + sessionToken, + type = 'fetch', +}) { const response = await fetch(applicationConfig.graphqlEndpoint, { method: 'POST', mode: 'cors', @@ -47,7 +53,7 @@ async function executeRequest({ sessionToken, query, variables }) { if (!response.ok) { const errorResponse = await getErrorResponse(response) console.error( - `Something wrong happened during => ${response.status}, ${ + `Something wrong happened during ${type} => ${response.status}, ${ response.statusText }: ${JSON.stringify(errorResponse)}` ) @@ -55,14 +61,22 @@ async function executeRequest({ sessionToken, query, variables }) { errorResponse && errorResponse.errors && errorResponse.errors.length ? errorResponse.errors[0].message : 'Unexpected error!' - throw new Error(errorMessage) + const error = new Error(errorMessage) + error.messages = errorResponse?.errors ?? [errorMessage] + throw error } - const json = await response.json() - if (json.errors) { - throw new Error(json.errors[0].message) + const body = await response.json() + if (body.errors) { + const errorMessage = + type === 'fetch' + ? 'Something wrong happened while fetching data.' + : 'Something wrong happened while mutating data.' + const error = new Error(errorMessage) + error.messages = body.errors + throw error } - return json.data + return body.data } export function useGraphQLClient() { @@ -74,13 +88,20 @@ export function useGraphQLClient() { } /** - * - * @param {string} sessionToken - * @param {DocumentNode|string} queryOrAST - * @param {{[string: key]: value}|undefined} variables + * @param {Object} context query context + * @param {DocumentNode|string} context.query request query (as AST or string) + * @param {{[string: key]: any}|undefined} context.variables request variables + * @param {string} context.sessionToken session token (for authentication) + * @param {'fetch'|'mutate'} [context.type='fetch'] request type (either fetch or mutate) * @returns {Promise} + * @throws Error if something went wrong */ -export function executeQuery({ sessionToken, query: queryOrAST, variables }) { +export function executeQuery({ + query: queryOrAST, + variables, + sessionToken, + type = 'fetch', +}) { const query = typeof queryOrAST === 'string' ? queryOrAST : print(queryOrAST) - return executeRequest({ query, variables, sessionToken }) + return executeRequest({ query, variables, sessionToken, type }) } diff --git a/front/src/hooks/contributor.js b/front/src/hooks/contributor.js new file mode 100644 index 000000000..87ac0f30d --- /dev/null +++ b/front/src/hooks/contributor.js @@ -0,0 +1,45 @@ +import { useSelector } from 'react-redux' +import { useSWRConfig } from 'swr' +import { executeQuery } from '../helpers/graphQL.js' +import { + addContributor as addContributorQuery, + removeContributor as removeContributorQuery, +} from '../components/ArticleContributors.graphql' +import { getArticleContributors } from '../components/Article.graphql' +import { useSWRKey } from './graphql.js' + +export function useArticleContributorActions({ articleId }) { + const { mutate } = useSWRConfig() + const key = useSWRKey()({ query: getArticleContributors }) + const sessionToken = useSelector((state) => state.sessionToken) + const addContributor = async (contributorId) => { + const response = await executeQuery({ + sessionToken, + query: addContributorQuery, + variables: { userId: contributorId, articleId }, + }) + await mutate(key, async (_) => ({ + article: { + contributors: response.article.addContributor.contributors, + }, + })) + } + + const removeContributor = async (contributorId) => { + const response = await executeQuery({ + sessionToken, + query: removeContributorQuery, + variables: { userId: contributorId, articleId }, + }) + await mutate(key, async (_) => ({ + article: { + contributors: response.article.removeContributor.contributors, + }, + })) + } + + return { + addContributor, + removeContributor, + } +} diff --git a/front/src/hooks/graphql.js b/front/src/hooks/graphql.js index 1030005e5..3be78bee9 100644 --- a/front/src/hooks/graphql.js +++ b/front/src/hooks/graphql.js @@ -1,44 +1,15 @@ -import useSWR, { preload } from 'swr' -import { useSelector } from 'react-redux' import { print } from 'graphql/language/printer' -import { applicationConfig } from '../config.js' - -async function fetcher({ query, variables, sessionToken }) { - return request({ query, variables, sessionToken }) -} +import { useSelector } from 'react-redux' +import useSWR from 'swr' +import { executeQuery } from '../helpers/graphQL.js' -async function request({ query, variables, sessionToken, type = 'fetch' }) { - const errorMessage = - type === 'fetch' - ? 'Something wrong happened while fetching data.' - : 'Something wrong happened while mutating data.' - const response = await fetch(applicationConfig.graphqlEndpoint, { - method: 'POST', - mode: 'cors', - credentials: 'omit', - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - // Authorization header is provided only when we have a token - ...(sessionToken ? { Authorization: `Bearer ${sessionToken}` } : {}), - }, - body: JSON.stringify({ query, variables }), +async function fetch({ query, variables, sessionToken }) { + return executeQuery({ + query, + variables, + sessionToken, + type: 'fetch', }) - - if (response.ok) { - const body = await response.json() - if (body.errors) { - const error = new Error(errorMessage) - error.messages = body.errors - throw error - } - return body.data - } - - const error = new Error(errorMessage) - error.info = await response.json().errors - error.status = response.status - throw error } /** @@ -53,22 +24,28 @@ export default function useGraphQL({ query: queryOrAST, variables }, options) { const sessionToken = useSelector((state) => state.sessionToken) const query = typeof queryOrAST === 'string' ? queryOrAST : print(queryOrAST) - return useSWR({ query, variables, sessionToken }, fetcher, options) + return useSWR({ query, variables, sessionToken }, fetch, options) } +/** + * @deprecated + */ export function useMutation() { const sessionToken = useSelector((state) => state.sessionToken) return runMutation.bind(null, { sessionToken }) } +/** + * @deprecated + */ export function runMutation( { sessionToken }, { query: queryOrAST, variables } ) { const query = typeof queryOrAST === 'string' ? queryOrAST : print(queryOrAST) - return request({ + return executeQuery({ query, variables, sessionToken, @@ -76,6 +53,9 @@ export function runMutation( }) } +/** + * @deprecated + */ export function useMutate({ query: queryOrAST, variables }) { const sessionToken = useSelector((state) => state.sessionToken) const query = typeof queryOrAST === 'string' ? queryOrAST : print(queryOrAST) @@ -91,12 +71,3 @@ export function useSWRKey() { return { query, variables, sessionToken } } } - -export function usePreload() { - const sessionToken = useSelector((state) => state.sessionToken) - return ({ query: queryOrAST, variables }) => { - const query = - typeof queryOrAST === 'string' ? queryOrAST : print(queryOrAST) - return preload({ query, variables, sessionToken }, fetcher) - } -} diff --git a/front/src/hooks/workspace.js b/front/src/hooks/workspace.js index cd13d8ee0..5dc62c67f 100644 --- a/front/src/hooks/workspace.js +++ b/front/src/hooks/workspace.js @@ -1,12 +1,12 @@ import { useSelector } from 'react-redux' import { useSWRConfig } from 'swr' import { + create as createMutation, getWorkspaceMembers, getWorkspaces, inviteMember as inviteMemberMutation, - removeMember as removeMemberMutation, - create as createMutation, leave as leaveMutation, + removeMember as removeMemberMutation, } from '../components/workspace/Workspaces.graphql' import { executeQuery } from '../helpers/graphQL.js' import useGraphQL, { useMutation, useSWRKey } from './graphql.js' @@ -110,34 +110,30 @@ export function useWorkspaceActions() { const key = useSWRKey()({ query: getWorkspaces }) const sessionToken = useSelector((state) => state.sessionToken) const addWorkspace = async (workspace) => { - await executeQuery( - { sessionToken }, - { - query: createMutation, - variables: { - data: { - color: workspace.color, - description: workspace.description, - name: workspace.name, - }, + await executeQuery({ + sessionToken, + query: createMutation, + variables: { + data: { + color: workspace.color, + description: workspace.description, + name: workspace.name, }, - } - ) + }, + }) await mutate(key, async (data) => ({ workspaces: [workspace, ...data.workspaces], })) } const leaveWorkspace = async (workspaceId) => { - await executeQuery( - { sessionToken }, - { - query: leaveMutation, - variables: { - workspaceId, - }, - } - ) + await executeQuery({ + sessionToken, + query: leaveMutation, + variables: { + workspaceId, + }, + }) await mutate( key, async (data) => ({