Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clearing a layer notifies subscribers #1074

Merged
merged 2 commits into from
Apr 28, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
clearing a layer notifies subscribers
  • Loading branch information
AlecAivazis committed Apr 28, 2023
commit 0d14f66ee7d9a07bf66739f9998275220d937444
31 changes: 29 additions & 2 deletions e2e/_api/graphql.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,25 @@ export const resolvers = {
},

Mutation: {
addNonNullUser(...args) {
return this.addUser(...args)
},
addUser: async (_, args) => {
const list = getUserSnapshot(args.snapshot)
if (args.delay) {
await sleep(args.delay)
}

if (args.force === 'NULL') {
// we don't want to handle a GraphQL error here. Just a null return
return null
}

if (args.force === 'ERROR') {
throw new GraphQLError('force ERROR!', { code: 501 })
}

const list = getUserSnapshot(args.snapshot)

const user = {
id: `${args.snapshot}:${list.length + 1}`,
name: args.name,
Expand Down Expand Up @@ -366,7 +380,20 @@ export const resolvers = {
city.libraries = city.libraries.filter((library) => library.id !== libraryId)
return library
},
deleteBook: (_, args) => {
deleteBook: async (_, args) => {
if (args.delay) {
await sleep(args.delay)
}

if (args.force === 'NULL') {
// we don't want to handle a GraphQL error here. Just a null return
return null
}

if (args.force === 'ERROR') {
throw new GraphQLError('force ERROR!', { code: 501 })
}

const bookId = Number.parseInt(args.book)
const city = cities.find((city) =>
city.libraries.find((library) => library.books.find((book) => book.id === bookId))
Expand Down
18 changes: 17 additions & 1 deletion e2e/_api/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,30 @@ enum TypeOfUser {
COOL
}

enum ForceReturn {
NORMAL
NULL
ERROR
}

type Mutation {
addUser(
birthDate: DateTime!
name: String!
snapshot: String!
enumValue: MyEnum
types: [TypeOfUser!]
delay: Int
force: ForceReturn
): User
addNonNullUser(
birthDate: DateTime!
name: String!
snapshot: String!
enumValue: MyEnum
types: [TypeOfUser!]
delay: Int
force: ForceReturn
): User!
updateUser(id: ID!, name: String, snapshot: String!, birthDate: DateTime, delay: Int): User!
singleUpload(file: File!): String!
Expand All @@ -31,7 +47,7 @@ type Mutation {
addBook(library: ID!, title: String!): Book!
deleteCity(city: ID!): City!
deleteLibrary(library: ID!): Library!
deleteBook(book: ID!): Book!
deleteBook(book: ID!, delay: Int, force: ForceReturn): Book
updateRentedBook(userId: String!, bookId: Int!, rate: Int!): RentedBook
}

Expand Down
2 changes: 1 addition & 1 deletion e2e/kit/src/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/favicon.png" />
<link rel="icon" type="image/png" href="https://houdinigraphql.com/images/logo.png" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/dark.css" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
Expand Down
4 changes: 4 additions & 0 deletions e2e/kit/src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
`);
</script>

<svelte:head>
<title>Houdini • e2e • SvelteKit</title>
</svelte:head>

<slot />

<Test />
Expand Down
58 changes: 51 additions & 7 deletions e2e/kit/src/routes/stores/mutation-opti-list/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import { graphql } from '$houdini';
import { graphql, type ForceReturn$options } from '$houdini';

$: query = graphql(`
query OptimisticUsersList @load {
Expand All @@ -10,16 +10,58 @@
`);

const addUser = graphql(`
mutation AddUserOptiList($name: String!, $birthDate: DateTime!) {
addUser(name: $name, birthDate: $birthDate, delay: 1000, snapshot: "mutation-opti-list") {
mutation AddUserOptiList($name: String!, $birthDate: DateTime!, $force: ForceReturn = NORMAL) {
addUser(
name: $name
birthDate: $birthDate
delay: 500
snapshot: "mutation-opti-list"
force: $force
) {
...OptimisticUsersList_insert
}
}
`);

async function add() {
const addNonNullUser = graphql(`
mutation AddNonNullUserOptiList(
$name: String!
$birthDate: DateTime!
$force: ForceReturn = NORMAL
) {
addNonNullUser(
name: $name
birthDate: $birthDate
delay: 500
snapshot: "mutation-opti-list"
force: $force
) {
...OptimisticUsersList_insert
}
}
`);

const addNonNull = async (force: ForceReturn$options = 'NORMAL') => {
await addNonNullUser.mutate(
{ name: 'JYC', birthDate: new Date('1986-11-07'), force },
{
optimisticResponse: {
addNonNullUser: {
id: '??? id ???',
name: '...optimisticResponse... I could have guessed JYC!'
}
}
}
);
};

const add = async (force: ForceReturn$options = 'NORMAL') => {
await addUser.mutate(
{ name: 'JYC', birthDate: new Date('1986-11-07') },
{
name: '...optimisticResponse... I could have guessed JYC!',
birthDate: new Date('1986-11-07'),
force
},
{
optimisticResponse: {
addUser: {
Expand All @@ -29,12 +71,14 @@
}
}
);
}
};
</script>

<h1>Mutation Opti List</h1>

<button id="mutate" on:click={add}>Add User</button>
<button id="mutate" on:click={() => add()}>Add User</button>
<button id="mutate-null" on:click={() => add('NULL')}>Add User (null)</button>
<button id="mutate-error" on:click={() => addNonNull('ERROR')}>Add User (error)</button>

<div id="result">
{$query.data?.usersList.map((user) => user.name).join(', ')}
Expand Down
41 changes: 38 additions & 3 deletions e2e/kit/src/routes/stores/mutation-opti-list/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { test } from '@playwright/test';
import { routes } from '../../../lib/utils/routes.js';
import { expect_to_be, goto, locator_click } from '../../../lib/utils/testsHelper.js';

// We put everything in one test because we are on the same snapshot. We will do null, error and success!
test('Add User + Optimistic + List', async ({ page }) => {
await goto(page, routes.Stores_Mutation_Opti_List);

Expand All @@ -12,7 +13,41 @@ test('Add User + Optimistic + List', async ({ page }) => {
'Bruce Willis, Samuel Jackson, Morgan Freeman, Tom Hanks, Will Smith, Harrison Ford, Eddie Murphy, Clint Eastwood'
);

// 1 Optimistic Response
// 1 Optimistic Response (null)
// Await the click to have optimisticResponse data in the store
await locator_click(page, `button[id="mutate-null"]`);

// with optimisticResponse, the data is updated before the real response
await expect_to_be(
page,
'Bruce Willis, Samuel Jackson, Morgan Freeman, Tom Hanks, Will Smith, Harrison Ford, Eddie Murphy, Clint Eastwood, ...optimisticResponse... I could have guessed JYC!'
);

// 2 Real Response (null)
await sleep(1000); // The fake delai is of 500 ms delay
await expect_to_be(
page,
'Bruce Willis, Samuel Jackson, Morgan Freeman, Tom Hanks, Will Smith, Harrison Ford, Eddie Murphy, Clint Eastwood'
);

// 3 Optimistic Response (error)
// Await the click to have optimisticResponse data in the store
await locator_click(page, `button[id="mutate-error"]`);

// with optimisticResponse, the data is updated before the real response
await expect_to_be(
page,
'Bruce Willis, Samuel Jackson, Morgan Freeman, Tom Hanks, Will Smith, Harrison Ford, Eddie Murphy, Clint Eastwood, ...optimisticResponse... I could have guessed JYC!'
);

// 4 Real Response (error)
await sleep(1000); // The fake delai is of 500 ms delay
await expect_to_be(
page,
'Bruce Willis, Samuel Jackson, Morgan Freeman, Tom Hanks, Will Smith, Harrison Ford, Eddie Murphy, Clint Eastwood'
);

// 5 Optimistic Response
// Await the click to have optimisticResponse data in the store
await locator_click(page, `button[id="mutate"]`);

Expand All @@ -22,8 +57,8 @@ test('Add User + Optimistic + List', async ({ page }) => {
'Bruce Willis, Samuel Jackson, Morgan Freeman, Tom Hanks, Will Smith, Harrison Ford, Eddie Murphy, Clint Eastwood, ...optimisticResponse... I could have guessed JYC!'
);

// 2 Real Response
await sleep(2000); // The fake delai is of 1 sec
// 6 Real Response
await sleep(1000); // The fake delai is of 500 ms delay
await expect_to_be(
page,
'Bruce Willis, Samuel Jackson, Morgan Freeman, Tom Hanks, Will Smith, Harrison Ford, Eddie Murphy, Clint Eastwood, JYC'
Expand Down
46 changes: 44 additions & 2 deletions e2e/kit/src/routes/stores/nested-list/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
GQL_AddBook,
GQL_DeleteCity,
GQL_DeleteLibrary,
GQL_DeleteBook
GQL_DeleteBook,
type ForceReturn$options,
GQL_RemoveBook
} from '$houdini';

$: browser && GQL_Cities.fetch();
Expand Down Expand Up @@ -54,12 +56,32 @@
target.value = '';
};

const removeBook = (event: Event) => {
const target = event?.target as HTMLButtonElement;
if (!target.dataset.id) {
return;
}
GQL_RemoveBook.mutate(
{
book: target.dataset.id,
force: (target.dataset.force as ForceReturn$options) ?? 'NORMAL'
},
{ optimisticResponse: { deleteBook: { id: target.dataset.id } } }
);
};

const deleteBook = (event: Event) => {
const target = event?.target as HTMLButtonElement;
if (!target.dataset.id) {
return;
}
GQL_DeleteBook.mutate({ book: target.dataset.id });
GQL_DeleteBook.mutate(
{
book: target.dataset.id,
force: (target.dataset.force as ForceReturn$options) ?? 'NORMAL'
},
{ optimisticResponse: { deleteBook: { id: target.dataset.id } } }
);
};
</script>

Expand All @@ -79,7 +101,20 @@
{#each library?.books ?? [] as book}
<li>
{book?.id}: {book?.title}
<button data-id={book?.id} on:click={removeBook}>Remove</button>
<button data-id={book?.id} data-force="NULL" on:click={removeBook}
>Remove (null)</button
>
<button data-id={book?.id} data-force="ERROR" on:click={removeBook}
>Remove (error)</button
>
<button data-id={book?.id} on:click={deleteBook}>Delete</button>
<button data-id={book?.id} data-force="NULL" on:click={deleteBook}
>Delete (null)</button
>
<button data-id={book?.id} data-force="ERROR" on:click={deleteBook}
>Delete (error)</button
>
</li>
{/each}
<li><input data-id={library?.id} on:change={addBook} /></li>
Expand All @@ -96,3 +131,10 @@
</ul>

<pre>{JSON.stringify($GQL_Cities?.data, null, 4)}</pre>

<style>
button {
font-size: small;
padding: 0.5rem;
}
</style>
4 changes: 2 additions & 2 deletions e2e/kit/src/routes/stores/nested-list/MUTATION.DeleteBook.gql
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mutation DeleteBook($book: ID!) {
deleteBook(book: $book) {
mutation DeleteBook($book: ID!, $force: ForceReturn = NORMAL) {
deleteBook(book: $book, force: $force, delay: 1000) {
id @Book_delete
}
}
5 changes: 5 additions & 0 deletions e2e/kit/src/routes/stores/nested-list/MUTATION.RemoveBook.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mutation RemoveBook($book: ID!, $force: ForceReturn = NORMAL) {
deleteBook(book: $book, force: $force, delay: 1000) {
...Book_List_remove @allLists
}
}
10 changes: 9 additions & 1 deletion e2e/next/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ import * as React from 'react'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<head></head>
<head>
<link
rel="icon"
type="image/png"
href="https://houdinigraphql.com/images/logo.png"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Houdini • e2e • Next</title>
</head>
<body>{children}</body>
</html>
)
Expand Down
4 changes: 2 additions & 2 deletions e2e/react-vite/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/png" href="https://houdinigraphql.com/images/logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<title>Houdini • e2e • React vite</title>
</head>
<body>
<div id="root"></div>
Expand Down
4 changes: 2 additions & 2 deletions e2e/svelte/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/png" href="https://houdinigraphql.com/images/logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Svelte + TS</title>
<title>Houdini • e2e • Svelte</title>
</head>
<body>
<div id="app"></div>
Expand Down
Loading