Skip to content

Commit

Permalink
Improve support for unions and interfaces (#747)
Browse files Browse the repository at this point in the history
* 🚸 UPDATE: tentative repro #731

* 🚸 NEW: e2e tests for union-result

* use inline snapshots for artifact tests

* subscription selection includes fields and type-dependent fields indepdently

* update tests

* fix type errors

* update snapshots

* fix unsubscribe tests

* fix few bugs

* all tests pass

* failing test for abstract fields

* can read and write abstract fields

* reading, writing, and subscribing to abstract selections

* simplify selection walk when subscribing

* update snapshots

* flattenSelection flattens inline fragments

* embed typeMap in abstract selection

* comment

* fix list operations

* dry up selection lookup for a type

* tweak

* avoid infinite recursion

* changeset

* remove unused imports

* ignore masking for generating selections

* only ignore masking for queries

* test for interface to interface inline fragment

* update snapshot

Co-authored-by: jycouet <[email protected]>
  • Loading branch information
AlecAivazis and jycouet authored Dec 9, 2022
1 parent 758683f commit 7a34399
Show file tree
Hide file tree
Showing 40 changed files with 18,063 additions and 12,456 deletions.
6 changes: 6 additions & 0 deletions .changeset/green-buckets-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'houdini': patch
'houdini-svelte': patch
---

Fix issue when working with unions and interfaces
28 changes: 28 additions & 0 deletions e2e/_api/graphql.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,34 @@ export const resolvers = {
cities: () => {
return cities
},
userNodesResult: async (_, args) => {
if (args.forceMessage) {
return {
message: `snapshot:${args.snapshot}`,
__typename: 'Message1',
}
}

const allData = [...getSnapshot(args.snapshot)]
return {
totalCount: allData.length,
nodes: allData.splice(args.offset || 0, args.limit || 10),
__typename: 'UserNodes',
}
},
userResult: async (_, args) => {
if (args.forceMessage) {
return {
message: `snapshot:${args.snapshot}`,
__typename: 'Message1',
}
}

const user = getSnapshot(args.snapshot).find(
(c) => c.id === `${args.snapshot}:${args.id}`
)
return { ...user, __typename: 'User' }
},
},

User: {
Expand Down
9 changes: 9 additions & 0 deletions e2e/_api/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ type Query {
userNodes(limit: Int = 4, offset: Int, snapshot: String!): UserNodes!
session: String
cities: [City]!
userNodesResult(snapshot: String!, forceMessage: Boolean!): UserNodesResult!
userResult(id: ID!, snapshot: String!, forceMessage: Boolean!): UserResult!
}

type Subscription {
Expand Down Expand Up @@ -107,3 +109,10 @@ type City {
name: String!
libraries: [Library]!
}

union UserNodesResult = UserNodes | Message1
union UserResult = User | Message1

type Message1 {
message: String!
}
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 @@ -8,6 +8,7 @@ export const routes = {
isFetching_without_load: '/isFetching/without_load',
isFetching_route_1: '/isFetching/route_1',
lists_all: '/lists-all?limit=15',
union_result: '/union-result',

Stores_SSR: '/stores/ssr',
Stores_Network: '/stores/network',
Expand Down
8 changes: 8 additions & 0 deletions e2e/sveltekit/src/routes/union-result/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script lang="ts">
import UserNodesResult from './UserNodesResult.svelte';
import UserResult from './UserResult.svelte';
</script>

<UserNodesResult />

<UserResult id="1" />
14 changes: 14 additions & 0 deletions e2e/sveltekit/src/routes/union-result/UserNodesResult.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
query UserNodesResult($forceMessage: Boolean!) {
userNodesResult(snapshot: "union-result", forceMessage: $forceMessage) {
... on UserNodes {
totalCount
nodes {
id
name
}
}
... on Message1 {
message
}
}
}
16 changes: 16 additions & 0 deletions e2e/sveltekit/src/routes/union-result/UserNodesResult.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script lang="ts">
import { CachePolicy, GQL_UserNodesResult } from '$houdini';
async function getAllUsers() {
await GQL_UserNodesResult.fetch({
variables: { forceMessage: false },
policy: CachePolicy.CacheAndNetwork
});
}
</script>

<button id="getAllUsers" on:click={getAllUsers}>GET AllUsers</button>

<div id="result">
<pre>{JSON.stringify($GQL_UserNodesResult?.data, null, 2)}</pre>
</div>
11 changes: 11 additions & 0 deletions e2e/sveltekit/src/routes/union-result/UserResult.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
query UserResult($id: ID!, $forceMessage: Boolean!) {
userResult(snapshot: "union-result", forceMessage: $forceMessage, id: $id) {
... on User {
id
name
}
... on Message1 {
message
}
}
}
16 changes: 16 additions & 0 deletions e2e/sveltekit/src/routes/union-result/UserResult.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script lang="ts">
import { CachePolicy, GQL_UserResult } from '$houdini';
export let id: string;
async function getUser() {
await GQL_UserResult.fetch({
variables: { id, forceMessage: false },
policy: CachePolicy.NetworkOnly
});
}
</script>

<button id="getUser" on:click={getUser}>GET User</button>

<pre>{JSON.stringify($GQL_UserResult?.data, null, 2)}</pre>
70 changes: 70 additions & 0 deletions e2e/sveltekit/src/routes/union-result/spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { expect, test } from '@playwright/test';
import { routes } from '../../lib/utils/routes.js';
import { expectToBe, expect_1_gql, goto } from '../../lib/utils/testsHelper.js';

test.describe('union-result', () => {
test('Get two stores and not resetting', async ({ page }) => {
await goto(page, routes.union_result);

// When we arrive on the page, we expect to see null in the result div
await expectToBe(page, 'null');

// we click on the button to getAllUsers
await expect_1_gql(page, 'button[id="getAllUsers"]');

const data = {
userNodesResult: {
totalCount: 8,
nodes: [
{
id: 'union-result:1',
name: 'Bruce Willis'
},
{
id: 'union-result:2',
name: 'Samuel Jackson'
},
{
id: 'union-result:3',
name: 'Morgan Freeman'
},
{
id: 'union-result:4',
name: 'Tom Hanks'
},
{
id: 'union-result:5',
name: 'Will Smith'
},
{
id: 'union-result:6',
name: 'Harrison Ford'
},
{
id: 'union-result:7',
name: 'Eddie Murphy'
},
{
id: 'union-result:8',
name: 'Clint Eastwood'
}
],
__typename: 'UserNodes'
}
};

// expect data (of AllUsers) to be displayed
await expectToBe(page, JSON.stringify(data, null, 2));

// we click on the button to getAllUsers
const res = await expect_1_gql(page, 'button[id="getUser"]');

// expect data (of User) to be returned
expect(res).toBe(
`{"data":{"userResult":{"__typename":"User","id":"union-result:1","name":"Bruce Willis"}}}`
);

// expect data (of AllUsers) to still be here and displayed
await expectToBe(page, JSON.stringify(data, null, 2));
});
});
5 changes: 4 additions & 1 deletion packages/houdini-svelte/src/runtime/stores/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ export class QueryStore<
const config = await this.getConfig()

// validate and prepare the request context for the current environment (client vs server)
const { policy, params, context } = await fetchParams(this.artifact, this.storeName, args)
// make a shallow copy of the args so we don't mutate the arguments that the user hands us
const { policy, params, context } = await fetchParams(this.artifact, this.storeName, {
...args,
})

// identify if this is a CSF or load
const isLoadFetch = Boolean('event' in params && params.event)
Expand Down
Loading

2 comments on commit 7a34399

@vercel
Copy link

@vercel vercel bot commented on 7a34399 Dec 9, 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 7a34399 Dec 9, 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-houdinigraphql.vercel.app
docs-next-git-main-houdinigraphql.vercel.app
docs-next-kohl.vercel.app

Please sign in to comment.