#11 Use nodeId (ID) instead of id (number) as identifier for apollo cache

This commit is contained in:
Christoph Lienhard 2020-12-28 13:57:45 +01:00
parent 8c450790c3
commit 524739cbbb
Signed by: christoph.lienhard
GPG key ID: 6B98870DDC270884
11 changed files with 155 additions and 142 deletions

View file

@ -0,0 +1,37 @@
import {MockedResponse} from "@apollo/client/testing";
import {loginMutation, LoginMutationResponse} from "./login";
export const loginMock: Array<MockedResponse<LoginMutationResponse>> = [
{
request: {
query: loginMutation,
variables: {
email: "test@email.com",
password: "password",
}
},
result: {
data: {
authenticate: {
jwtToken: "123"
}
}
},
},
{
request: {
query: loginMutation,
variables: {
email: "test@email.com",
password: "wrong-password",
}
},
result: {
data: {
authenticate: {
jwtToken: null
}
}
},
}
]

View file

@ -0,0 +1,19 @@
import {gql} from "@apollo/client";
export const loginMutation = gql`
mutation Login($email: String!, $password: String!) {
authenticate(input: {email: $email, password: $password}) {
jwtToken
}
}`
export interface LoginMutationVariables {
email: string,
password: string
}
export interface LoginMutationResponse {
authenticate: {
jwtToken: string | null
}
}

View file

@ -0,0 +1,26 @@
import {gql} from "@apollo/client";
export const registerMutation = gql`
mutation CreateAccount($firstName: String!, $lastName: String!, $email: String!, $password: String!) {
registerPerson(input: {firstName: $firstName, lastName: $lastName, email: $email, password: $password}) {
person {
nodeId
}
}
}
`
export interface RegisterMutationVariables {
firstName: string,
lastName: string,
email: string,
password: string,
}
export interface RegisterMutationResponse {
registerPerson: {
person: {
nodeId: string
}
}
}

View file

@ -0,0 +1,25 @@
import {gql} from "@apollo/client";
export const allCategoriesQuery = gql`
query AllQuestions {
allCategories {
nodes {
nodeId
title
description
}
}
}
`
export interface AllCategoriesQueryResponse {
allCategories: {
nodes: Array<AllCategoriesQueryNodes>
}
}
interface AllCategoriesQueryNodes {
nodeId: string,
title: string,
description: string,
}

View file

@ -0,0 +1,31 @@
import {gql} from "@apollo/client";
export const allQuestionsQuery = gql`
query AllQuestions {
allQuestions {
nodes {
nodeId
text
description
categoryByCategoryId {
title
}
}
}
}
`
export interface AllQuestionsQueryResponse {
allQuestions: {
nodes: Array<AllQuestionsQueryNodes>
}
}
interface AllQuestionsQueryNodes {
nodeId: string,
text: string,
description: string,
categoryByCategoryId: {
title: string
}
}

View file

@ -1,9 +1,10 @@
import {Paper, Typography} from "@material-ui/core";
import React from "react";
import {makeStyles} from "@material-ui/core/styles";
import {gql, useQuery} from "@apollo/client";
import {useQuery} from "@apollo/client";
import AddCard from "./AddCard";
import AccordionWithEdit from "./AccordionWithEdit";
import {allCategoriesQuery, AllCategoriesQueryResponse} from "../backend/queries/allCategories";
const useStyles = makeStyles((theme) => ({
root: {
@ -21,7 +22,7 @@ export default function CategoryList() {
<Paper className={classes.root}>
<Typography component={"h2"} variant="h6" color="primary" gutterBottom>Kategorien</Typography>
{data?.allCategories.nodes.map(category => <AccordionWithEdit
key={category.id.toString()}
key={category.nodeId}
title={category.title}
description={category.description}/>
)}
@ -30,26 +31,3 @@ export default function CategoryList() {
)
}
const allCategoriesQuery = gql`
query AllQuestions {
allCategories {
nodes {
id
title
description
}
}
}
`
interface AllCategoriesQueryResponse {
allCategories: {
nodes: Array<AllCategoriesQueryNodes>
}
}
interface AllCategoriesQueryNodes {
id: number,
title: string,
description: string,
}

View file

@ -1,9 +1,10 @@
import {Paper, Typography} from "@material-ui/core";
import React from "react";
import {makeStyles} from "@material-ui/core/styles";
import {gql, useQuery} from "@apollo/client";
import {useQuery} from "@apollo/client";
import AddCard from "./AddCard";
import AccordionWithEdit from "./AccordionWithEdit";
import {allQuestionsQuery, AllQuestionsQueryResponse} from "../backend/queries/allQuestions";
const useStyles = makeStyles((theme) => ({
root: {
@ -21,7 +22,7 @@ export default function QuestionList() {
<Paper className={classes.root}>
<Typography component={"h2"} variant="h6" color="primary" gutterBottom>Fragen</Typography>
{data?.allQuestions.nodes.map(question => <AccordionWithEdit
key={question.id.toString()}
key={question.nodeId}
title={question.text}
subTitle={question.categoryByCategoryId.title}
description={question.description}/>
@ -31,32 +32,3 @@ export default function QuestionList() {
)
}
const allQuestionsQuery = gql`
query AllQuestions {
allQuestions {
nodes {
id
text
description
categoryByCategoryId {
title
}
}
}
}
`
interface AllQuestionsQueryResponse {
allQuestions: {
nodes: Array<AllQuestionsQueryNodes>
}
}
interface AllQuestionsQueryNodes {
id: number,
text: string,
description: string,
categoryByCategoryId: {
title: string
}
}

View file

@ -12,9 +12,10 @@ import {Link, useHistory, useLocation} from 'react-router-dom';
import Typography from '@material-ui/core/Typography';
import {makeStyles} from '@material-ui/core/styles';
import Container from '@material-ui/core/Container';
import {gql, useMutation} from "@apollo/client";
import {useMutation} from "@apollo/client";
import ButtonWithSpinner from "./ButtonWithSpinner";
import {Copyright} from "./Copyright";
import {loginMutation, LoginMutationResponse, LoginMutationVariables} from "../backend/mutations/login";
const useStyles = makeStyles((theme) => ({
paper: {
@ -152,24 +153,6 @@ export default function SignIn() {
);
}
export const loginMutation = gql`
mutation Login($email: String!, $password: String!) {
authenticate(input: {email: $email, password: $password}) {
jwtToken
}
}`
interface LoginMutationVariables {
email: string,
password: string
}
export interface LoginMutationResponse {
authenticate: {
jwtToken: string | null
}
}

View file

@ -10,10 +10,11 @@ import Typography from '@material-ui/core/Typography';
import {makeStyles} from '@material-ui/core/styles';
import Container from '@material-ui/core/Container';
import {Copyright} from "./Copyright";
import {gql, useMutation} from "@apollo/client";
import {useMutation} from "@apollo/client";
import ButtonWithSpinner from "./ButtonWithSpinner";
import {errorHandler, SignUpError} from "./SignUpErrorHandler";
import {Alert} from "@material-ui/lab";
import {registerMutation, RegisterMutationResponse, RegisterMutationVariables} from "../backend/mutations/signUp";
const useStyles = makeStyles((theme) => ({
@ -199,28 +200,3 @@ export default function SignUp() {
);
}
const registerMutation = gql`
mutation CreateAccount($firstName: String!, $lastName: String!, $email: String!, $password: String!) {
registerPerson(input: {firstName: $firstName, lastName: $lastName, email: $email, password: $password}) {
person {
nodeId
}
}
}
`
interface RegisterMutationVariables {
firstName: string,
lastName: string,
email: string,
password: string,
}
interface RegisterMutationResponse {
registerPerson: {
person: {
nodeId: string
}
}
}

View file

@ -4,7 +4,7 @@ import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {ApolloProvider} from "@apollo/client";
import {client} from "./backend-connection/helper";
import {client} from "./backend/helper";
import {BrowserRouter as Router} from "react-router-dom";
ReactDOM.render(

View file

@ -1,8 +1,9 @@
import React from 'react';
import SignIn, {loginMutation, LoginMutationResponse} from "../components/SignIn";
import SignIn from "../components/SignIn";
import {fireEvent, render, screen, waitFor} from '@testing-library/react'
import {MockedProvider, MockedResponse} from '@apollo/client/testing';
import {MockedProvider} from '@apollo/client/testing';
import {MemoryRouter} from 'react-router-dom';
import {loginMock} from "../backend/mutations/login.mock";
const mockHistoryReplace = jest.fn();
@ -13,46 +14,11 @@ jest.mock('react-router-dom', () => ({
}),
}));
const mocks: Array<MockedResponse<LoginMutationResponse>> = [
{
request: {
query: loginMutation,
variables: {
email: "test@email.com",
password: "password",
}
},
result: {
data: {
authenticate: {
jwtToken: "123"
}
}
},
},
{
request: {
query: loginMutation,
variables: {
email: "test@email.com",
password: "wrong-password",
}
},
result: {
data: {
authenticate: {
jwtToken: null
}
}
},
}
]
describe('SignIn page', () => {
beforeEach(() => mockHistoryReplace.mockReset())
test('initial state', () => {
render(<MockedProvider mocks={mocks}><MemoryRouter><SignIn/></MemoryRouter></MockedProvider>);
render(<MockedProvider mocks={loginMock}><MemoryRouter><SignIn/></MemoryRouter></MockedProvider>);
// it renders empty email and passsword fields
const emailField = screen.getByRole('textbox', {name: 'Email Address'});
@ -68,7 +34,7 @@ describe('SignIn page', () => {
});
test('successful login', async () => {
render(<MockedProvider mocks={mocks}><MemoryRouter><SignIn/></MemoryRouter></MockedProvider>);
render(<MockedProvider mocks={loginMock}><MemoryRouter><SignIn/></MemoryRouter></MockedProvider>);
const emailField = screen.getByRole('textbox', {name: 'Email Address'});
const passwordField = screen.getByLabelText(/Password/);
@ -86,7 +52,7 @@ describe('SignIn page', () => {
});
test('error login', async () => {
render(<MockedProvider mocks={mocks}><MemoryRouter><SignIn/></MemoryRouter></MockedProvider>);
render(<MockedProvider mocks={loginMock}><MemoryRouter><SignIn/></MemoryRouter></MockedProvider>);
const emailField = screen.getByRole('textbox', {name: 'Email Address'});
const passwordField = screen.getByLabelText(/Password/);