Skip to content

Commit

Permalink
feat: custom invalidation indices (#790)
Browse files Browse the repository at this point in the history
* feat: custom invalidation indices

* chore: add changeset

* rebase adjustments

* fix: dispose unexpected stream results

* changelog wording adjustments
  • Loading branch information
n1ru4l committed Dec 31, 2021
1 parent 727e806 commit aee5d58
Show file tree
Hide file tree
Showing 4 changed files with 291 additions and 26 deletions.
62 changes: 62 additions & 0 deletions .changeset/strange-kangaroos-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
"@n1ru4l/in-memory-live-query-store": minor
---

Allow setting custom invalidation indices.

Until now doing granular or very specific index invalidations wasn't possible. Thus invalidation might not have been efficient enough, as either too many or too few live query operation "subscriptions" got invalidated.

The new `indexBy` configuration option for the `InMemoryLiveQueryStore`, allows configuring specific indices suitable for the consumed GraphQL schema, resulting more granular and efficient invalidations.

**Invalidate by single field with arguments:**

```ts
const store = new InMemoryLiveQueryStore({
includeIdentifierExtension: true,
indexBy: [
{
field: "Query.posts",
args: ["needle"],
},
],
});

const execute = store.makeExecute(executeImplementation);

const document = parse(/* GraphQL */ `
query @live {
posts(needle: "skrrrrt") {
id
title
}
}
`);

const executionResult = execute({ document, schema });

let result = await executionResult.next();
expect(result.value).toEqual({
data: {
posts: [],
},
extensions: {
liveResourceIdentifier: ["Query.posts", 'Query.posts(needle:"skrrrrt")'],
},
isLive: true,
});
```

**Invalidation by single field with specific arguments:**

```ts
const store = new InMemoryLiveQueryStore({
includeIdentifierExtension: true,
indexBy: [
{
field: "Query.posts",
// index will only be used if the needle argument value equals "brrrrt"
args: [["needle", "brrrrt"]],
},
],
});
```
143 changes: 140 additions & 3 deletions packages/in-memory-live-query-store/src/InMemoryLiveQueryStore.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
GraphQLSchema,
GraphQLString,
parse,
execute,
execute as executeImplementation,
GraphQLList,
} from "graphql";
import { isAsyncIterable } from "@graphql-tools/utils";
import { InMemoryLiveQueryStore } from "./InMemoryLiveQueryStore";
Expand Down Expand Up @@ -47,13 +48,18 @@ const createTestSchema = (
id: string;
title: string;
};
posts?: Array<{
id: string;
title: string;
}>;
} = {
query: "queried",
mutation: "mutated",
post: {
id: "1",
title: "lel",
},
posts: [],
}
) => {
const GraphQLPostType = new GraphQLObjectType({
Expand Down Expand Up @@ -83,6 +89,18 @@ const createTestSchema = (
},
resolve: () => mutableSource.post,
},
posts: {
type: new GraphQLList(GraphQLPostType),
args: {
needle: {
type: GraphQLString,
},
whereAuthorId: {
type: GraphQLID,
},
},
resolve: () => mutableSource.posts,
},
},
});
const Mutation = new GraphQLObjectType({
Expand Down Expand Up @@ -797,10 +815,14 @@ it("makeExecute calls the execute it is passed to resolve live queries", async (
`);

const executePassedAtInitializationTime = jest.fn();
executePassedAtInitializationTime.mockImplementation((args) => execute(args));
executePassedAtInitializationTime.mockImplementation((args) =>
executeImplementation(args)
);

const executePassedToMakeExecute = jest.fn();
executePassedToMakeExecute.mockImplementation((args) => execute(args));
executePassedToMakeExecute.mockImplementation((args) =>
executeImplementation(args)
);

const store = new InMemoryLiveQueryStore({
execute: executePassedAtInitializationTime,
Expand All @@ -820,3 +842,118 @@ it("makeExecute calls the execute it is passed to resolve live queries", async (
expect(executePassedToMakeExecute).toHaveBeenCalled();
await result.return?.();
});

it("index via custom index field of type string", async () => {
const schema = createTestSchema();

const store = new InMemoryLiveQueryStore({
includeIdentifierExtension: true,
indexBy: [
{
field: "Query.posts",
args: ["needle"],
},
],
});

const execute = store.makeExecute(executeImplementation);

const document = parse(/* GraphQL */ `
query @live {
posts(needle: "brrrrrrt") {
id
title
}
}
`);

const executionResult = execute({ document, schema });
assertAsyncIterable(executionResult);
let result = await executionResult.next();
expect(result.value).toEqual({
data: {
posts: [],
},
extensions: {
liveResourceIdentifier: ["Query.posts", 'Query.posts(needle:"brrrrrrt")'],
},
isLive: true,
});
});

it("index via custom index field with string value", async () => {
const schema = createTestSchema();

const store = new InMemoryLiveQueryStore({
includeIdentifierExtension: true,
indexBy: [
{
field: "Query.posts",
args: [["needle", "sup"]],
},
],
});
const execute = store.makeExecute(executeImplementation);

const document = parse(/* GraphQL */ `
query @live {
posts(needle: "sup") {
id
title
}
}
`);

const executionResult = execute({ document, schema });
assertAsyncIterable(executionResult);
let result = await executionResult.next();
expect(result.value).toEqual({
data: {
posts: [],
},
extensions: {
liveResourceIdentifier: ["Query.posts", 'Query.posts(needle:"sup")'],
},
isLive: true,
});
});

it("index via custom compound index", async () => {
const schema = createTestSchema();

const store = new InMemoryLiveQueryStore({
includeIdentifierExtension: true,
indexBy: [
{
field: "Query.posts",
args: ["whereAuthorId", "needle"],
},
],
});
const execute = store.makeExecute(executeImplementation);

const document = parse(/* GraphQL */ `
query @live {
posts(needle: "sup", whereAuthorId: "3") {
id
title
}
}
`);

const executionResult = execute({ document, schema });
assertAsyncIterable(executionResult);
let result = await executionResult.next();
expect(result.value).toEqual({
data: {
posts: [],
},
extensions: {
liveResourceIdentifier: [
"Query.posts",
'Query.posts(whereAuthorId:"3",needle:"sup")',
],
},
isLive: true,
});
});

0 comments on commit aee5d58

Please sign in to comment.