Skip to content

Commit

Permalink
Add @alllists directive (#693)
Browse files Browse the repository at this point in the history
* 🚧 FIX: api & vscode plugin settings

* 🐛 FIX: query with custom first args

* ✨ NEW: add @allList directive

* ✏️ DOC: allList in changeset

* Update packages/houdini/src/codegen/generators/artifacts/artifacts.test.ts

Co-authored-by: Alec Aivazis <[email protected]>

* ✏️ UPDATE: allList => AllLists

* 👌 UPDATE: append && prepend

* ✏️ FIX: naming

* ✨ IMPROVE: feat + start e2e

* 👌 UPDATE: artefacts => typecheck

* 👌 UPDATE: tests

* ✅ UPDATE: e2e tests

* ⬆️ UPDATE: dep sveltekit

* 🐛 FIX: type

* @parentID is ignored when there's one list that doesn't match

* remove ineffective parentID in operations

* strengthen test

* config

Co-authored-by: Alec Aivazis <[email protected]>
  • Loading branch information
jycouet and AlecAivazis authored Nov 16, 2022
1 parent fe6f5ba commit 6e36775
Show file tree
Hide file tree
Showing 33 changed files with 1,132 additions and 428 deletions.
5 changes: 5 additions & 0 deletions .changeset/stale-ligers-appear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'houdini': patch
---

Adding a new directive @allLists to update all lists after a mutation
10 changes: 5 additions & 5 deletions .graphqlrc.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
projects:
default:
sveltekit:
# 👇 For vscode-graphql and intellisense
schema:
- e2e/api/*.graphql
- e2e/$houdini/graphql/schema.graphql
- e2e/_api/*.graphql
- e2e/sveltekit/$houdini/graphql/schema.graphql
documents:
- e2e/src/**/*.gql
- e2e/$houdini/graphql/documents.gql
- e2e/sveltekit/src/**/*.gql
- e2e/sveltekit/$houdini/graphql/documents.gql
113 changes: 68 additions & 45 deletions e2e/_api/graphql.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,46 @@ export const typeDefs = sourceFiles.map((filepath) =>

// Example Cities/Libraries/Books data
// Assume a traditional relational database for storage - each table with unique ID.
let cityId = 1;
let libraryId = 1;
let bookId = 1;
let cityId = 1
let libraryId = 1
let bookId = 1

// Allow the "database" to be persistent and mutable
let cities = [
{
id: cityId++, name: 'Alexandria', libraries: [
id: cityId++,
name: 'Alexandria',
libraries: [
{
id: libraryId++, name: 'The Library of Alexandria', books: [
id: libraryId++,
name: 'The Library of Alexandria',
books: [
{ id: bookId++, title: 'Callimachus Pinakes' },
{ id: bookId++, title: 'Kutubkhana-i-lskandriyya' },
]
],
},
{
id: libraryId++, name: 'Bibliotheca Alexandrina', books: [
{ id: bookId++, title: 'Analyze your own personality' },
]
id: libraryId++,
name: 'Bibliotheca Alexandrina',
books: [{ id: bookId++, title: 'Analyze your own personality' }],
},
]
],
},
{
id: cityId++, name: 'Istanbul', libraries: [
id: cityId++,
name: 'Istanbul',
libraries: [
{
id: libraryId++, name: 'The Imperial Library of Constantinople', books: [
id: libraryId++,
name: 'The Imperial Library of Constantinople',
books: [
{ id: bookId++, title: 'Homer' },
{ id: bookId++, title: 'The Hellenistic History' },
]
],
},
]
],
},
];
]

// example data
const data = [
Expand Down Expand Up @@ -88,6 +96,13 @@ export const resolvers = {
usersList: (_, args) => {
return [...getSnapshot(args.snapshot)].splice(args.offset || 0, args.limit)
},
userNodes: (_, args) => {
const allData = [...getSnapshot(args.snapshot)]
return {
totalCount: allData.length,
nodes: allData.splice(args.offset || 0, args.limit),
}
},
session: (_, args, info) => {
let token = null
info.request.headers.forEach((value, key) => {
Expand Down Expand Up @@ -136,7 +151,7 @@ export const resolvers = {
}
},
cities: () => {
return cities;
return cities
},
},

Expand All @@ -156,7 +171,7 @@ export const resolvers = {
await sleep(args.delay)
}
const user = {
id: (list.length + 1).toString(),
id: `${args.snapshot}:${list.length + 1}`,
name: args.name,
birthDate: args.birthDate,
enumValue: args.enumValue,
Expand Down Expand Up @@ -187,7 +202,7 @@ export const resolvers = {
try {
let data = await processFile(file)
return data
} catch (e) { }
} catch (e) {}
throw new GraphQLYogaError('ERROR', { code: 500 })
},
multipleUpload: async (_, { files }) => {
Expand All @@ -209,12 +224,12 @@ export const resolvers = {
libraries: [],
}

cities.push(city);
return city;
cities.push(city)
return city
},
addLibrary: (_, args) => {
const cityId = Number.parseInt(args.city);
const city = cities.find((city) => city.id === cityId);
const cityId = Number.parseInt(args.city)
const city = cities.find((city) => city.id === cityId)
if (!city) {
throw new GraphQLYogaError('City not found', { code: 404 })
}
Expand All @@ -224,50 +239,58 @@ export const resolvers = {
name: args.name,
books: [],
}
city.libraries.push(library);
return library;
city.libraries.push(library)
return library
},
addBook: (_, args) => {
const libraryId = Number.parseInt(args.library);
const city = cities.find((city) => city.libraries.find((library) => library.id === libraryId));
const libraryId = Number.parseInt(args.library)
const city = cities.find((city) =>
city.libraries.find((library) => library.id === libraryId)
)
if (!city) {
throw new GraphQLYogaError('City/Library not found', { code: 404 })
}
const library = city.libraries.find((library) => library.id === libraryId);
const library = city.libraries.find((library) => library.id === libraryId)

const book = {
id: bookId++,
title: args.title,
}
library.books.push(book);
return book;
library.books.push(book)
return book
},
deleteCity: (_, args) => {
const cityId = Number.parseInt(args.city);
const city = cities.find((city) => city.id === cityId);
cities = cities.filter((city) => city.id !== cityId);
return city;
const cityId = Number.parseInt(args.city)
const city = cities.find((city) => city.id === cityId)
cities = cities.filter((city) => city.id !== cityId)
return city
},
deleteLibrary: (_, args) => {
const libraryId = Number.parseInt(args.library);
const city = cities.find((city) => city.libraries.find((library) => library.id === libraryId));
const libraryId = Number.parseInt(args.library)
const city = cities.find((city) =>
city.libraries.find((library) => library.id === libraryId)
)
if (!city) {
throw new GraphQLYogaError('City/Library not found', { code: 404 })
}
const library = city.libraries.find((library) => library.id === libraryId);
city.libraries = city.libraries.filter((library) => library.id !== libraryId);
return library;
const library = city.libraries.find((library) => library.id === libraryId)
city.libraries = city.libraries.filter((library) => library.id !== libraryId)
return library
},
deleteBook: (_, args) => {
const bookId = Number.parseInt(args.book);
const city = cities.find((city) => city.libraries.find((library) => library.books.find((book) => book.id === bookId)));
const bookId = Number.parseInt(args.book)
const city = cities.find((city) =>
city.libraries.find((library) => library.books.find((book) => book.id === bookId))
)
if (!city) {
throw new GraphQLYogaError('City/Library/Book not found', { code: 404 })
}
const library = city.libraries.find((library) => library.books.find((book) => book.id === bookId));
const book = library.books.find((book) => book.id === bookId);
library.books = library.books.filter((book) => book.id !== bookId);
return book;
const library = city.libraries.find((library) =>
library.books.find((book) => book.id === bookId)
)
const book = library.books.find((book) => book.id === bookId)
library.books = library.books.filter((book) => book.id !== bookId)
return book
},
},

Expand Down
6 changes: 6 additions & 0 deletions e2e/_api/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ type Query {
snapshot: String!
): UserConnection!
usersList(limit: Int = 4, offset: Int, snapshot: String!): [User!]!
userNodes(limit: Int = 4, offset: Int, snapshot: String!): UserNodes!
session: String
cities: [City]!
}
Expand Down Expand Up @@ -85,6 +86,11 @@ type UserEdge {
node: User
}

type UserNodes {
totalCount: Int
nodes: [User!]!
}

type Book {
id: ID!
title: String!
Expand Down
4 changes: 2 additions & 2 deletions e2e/sveltekit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
"devDependencies": {
"@kitql/helper": "^0.5.0",
"@playwright/test": "1.25.0",
"@sveltejs/adapter-auto": "1.0.0-next.66",
"@sveltejs/kit": "1.0.0-next.510",
"@sveltejs/adapter-auto": "1.0.0-next.88",
"@sveltejs/kit": "1.0.0-next.547",
"@typescript-eslint/eslint-plugin": "^5.10.1",
"@typescript-eslint/parser": "^5.10.1",
"concurrently": "7.1.0",
Expand Down
1 change: 1 addition & 0 deletions e2e/sveltekit/src/lib/utils/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const routes = {
Query_param: '/query-param',
isFetching_with_load: '/isFetching/with_load',
isFetching_without_load: '/isFetching/without_load',
lists_all: '/lists-all?limit=15',

Stores_SSR: '/stores/ssr',
Stores_Network: '/stores/network',
Expand Down
10 changes: 8 additions & 2 deletions e2e/sveltekit/src/lib/utils/testsHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export async function expect_n_gql(
page: Page,
selector: string | null,
n: number,
action: 'click' | 'hover' = 'click'
action: 'click' | 'hover' | 'press_ArrowUp' | 'press_ArrowDown' = 'click'
) {
const start = new Date().valueOf();
const timing: number[] = [];
Expand Down Expand Up @@ -81,8 +81,14 @@ export async function expect_n_gql(
if (selector) {
if (action === 'click') {
await page.click(selector);
} else {
} else if (action === 'hover') {
await page.hover(selector);
} else if (action === 'press_ArrowUp') {
await page.locator(selector).press('ArrowUp');
} else if (action === 'press_ArrowDown') {
page.locator(selector).press('ArrowDown');
} else {
throw new Error(`action ${action} not implemented`);
}
}

Expand Down
9 changes: 9 additions & 0 deletions e2e/sveltekit/src/routes/lists-all/+page.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
query ListAll($limit: Int!) {
userNodes(limit: $limit, snapshot: "lists-all") {
totalCount
nodes @list(name: "List_All") {
id
name
}
}
}
37 changes: 37 additions & 0 deletions e2e/sveltekit/src/routes/lists-all/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<script lang="ts">
import { browser } from '$app/environment';
import { goto, invalidate } from '$app/navigation';
import { page } from '$app/stores';
import { GQL_ListAll_AddUser } from '$houdini';
import type { PageData } from './$houdini';
export let data: PageData;
$: ({ ListAll } = data);
let limit = parseInt($page.url.searchParams.get('limit') ?? '1', 10);
$: browser && limit && updateQS();
async function updateQS() {
$page.url.searchParams.set('limit', limit.toString());
const newUrl = $page.url.href;
await invalidate(newUrl);
await goto(newUrl, { replaceState: true, keepFocus: true });
}
const add = async () => {
await GQL_ListAll_AddUser.mutate(null);
};
</script>

<input type="number" bind:value={limit} />
<br />
<br />
<button on:click={add}>Add User</button>

<h2>List</h2>
<div id="result">
{#each $ListAll.data?.userNodes.nodes ?? [] as user}
<div>{user?.name}</div>
{/each}
</div>
10 changes: 10 additions & 0 deletions e2e/sveltekit/src/routes/lists-all/+page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { PageLoad } from './$types';
import { load_ListAll } from '$houdini';

export const load: PageLoad = async (event) => {
const limit = parseInt(event.url.searchParams.get('limit') ?? '1', 10);

return {
...(await load_ListAll({ event, variables: { limit } }))
};
};
5 changes: 5 additions & 0 deletions e2e/sveltekit/src/routes/lists-all/AddUser.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mutation ListAll_AddUser {
addUser(name: "Omar Sy", birthDate: 254143016000, snapshot: "List_All") {
...List_All_insert @allLists @prepend
}
}
44 changes: 44 additions & 0 deletions e2e/sveltekit/src/routes/lists-all/spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { test } from '@playwright/test';
import { routes } from '../../lib/utils/routes.js';
import { expectToBe, expect_n_gql, goto } from '../../lib/utils/testsHelper.js';

test.describe('lists-all', () => {
test('With 3 lists to append', async ({ page }) => {
await goto(page, routes.lists_all);

// select the input
await page.locator('input[type="number"]').click();
// add 1 to the input to load a second list
await expect_n_gql(page, 'input[type="number"]', 1, 'press_ArrowUp');
// add 1 to the input to load a third list
await expect_n_gql(page, 'input[type="number"]', 1, 'press_ArrowUp');

// expect to have the righ data
await expectToBe(
page,
'Bruce WillisSamuel JacksonMorgan FreemanTom HanksWill SmithHarrison FordEddie MurphyClint Eastwood'
);

// mutation to add a new actor (Expect to have 1 mutation)
await expect_n_gql(page, 'text=Add User', 1);

// expect to have the data added
await expectToBe(
page,
'Omar SyBruce WillisSamuel JacksonMorgan FreemanTom HanksWill SmithHarrison FordEddie MurphyClint Eastwood'
);

// select the input
await page.locator('input[type="number"]').click();
// go back 1 list, expect no graphql request (from cache!)
await expect_n_gql(page, 'input[type="number"]', 0, 'press_ArrowDown');
// go back 1 list, expect no graphql request (from cache!)
await expect_n_gql(page, 'input[type="number"]', 0, 'press_ArrowDown');

// expect the data to still contain the new actor
await expectToBe(
page,
'Omar SyBruce WillisSamuel JacksonMorgan FreemanTom HanksWill SmithHarrison FordEddie MurphyClint Eastwood'
);
});
});
Loading

2 comments on commit 6e36775

@vercel
Copy link

@vercel vercel bot commented on 6e36775 Nov 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 6e36775 Nov 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

docs-next – ./site

docs-next-kohl.vercel.app
docs-next-git-main-houdinigraphql.vercel.app
docs-next-houdinigraphql.vercel.app

Please sign in to comment.