Skip to content

Commit

Permalink
feat: example of using apollo with patch (#950)
Browse files Browse the repository at this point in the history
* feat: example of using apollo with patch

* feat: use jsondiffpatch everywhere
  • Loading branch information
n1ru4l authored Jul 29, 2022
1 parent 2d80246 commit 25ad6d0
Show file tree
Hide file tree
Showing 35 changed files with 443 additions and 115 deletions.
5 changes: 5 additions & 0 deletions .changeset/thin-pumas-turn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@n1ru4l/graphql-live-query-patch": patch
---

Ensure the `data` property reference changes for each published value in order to please GraphQL clients that rely on immutability.
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const createApplyLiveQueryPatch =
mutableData as Record<string, unknown>,
next.value.patch
);
valueToPublish.data = mutableData as Record<string, unknown>;
valueToPublish.data = { ...mutableData } as Record<string, unknown>;

lastRevision++;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/todo-example/client-apollo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
"private": true,
"dependencies": {
"@apollo/client": "3.6.9",
"@n1ru4l/graphql-live-query-patch-json-patch": "*",
"@n1ru4l/graphql-live-query-patch-jsondiffpatch": "0.7.0",
"@n1ru4l/push-pull-async-iterable-iterator": "3.2.0",
"@n1ru4l/socket-io-graphql-client": "*",
"@repeaterjs/repeater": "3.0.4",
"@app/gql": "link:./src/gql",
"graphql": "16.0.0-experimental-stream-defer.5"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,34 @@ import {
Operation,
FetchResult,
Observable,
ApolloClient,
InMemoryCache,
gql,
} from "@apollo/client/core";
import { split } from "@apollo/client/link/core";
import { HttpLink } from "@apollo/client/link/http";
import { isLiveQueryOperationDefinitionNode } from "@n1ru4l/graphql-live-query";
import { Repeater } from "@repeaterjs/repeater";
import { print, getOperationAST } from "graphql";
import { applySourceToSink } from "./shared";

type SSELinkOptions = EventSourceInit & { uri: string };

function makeEventStreamSource(url: string, options: SSELinkOptions) {
return new Repeater<FetchResult>(async (push, end) => {
const eventsource = new EventSource(url, options);
eventsource.onmessage = function (event) {
const data = JSON.parse(event.data);
push(data);
if (eventsource.readyState === 2) {
end();
}
};
eventsource.onerror = function (error) {
end(error);
};
await end;
eventsource.close();
});
}

class SSELink extends ApolloLink {
constructor(private options: SSELinkOptions) {
super();
Expand All @@ -34,20 +51,12 @@ class SSELink extends ApolloLink {
);
}

return new Observable((sink) => {
const eventsource = new EventSource(url.toString(), this.options);
eventsource.onmessage = function (event) {
const data = JSON.parse(event.data);
sink.next(data);
if (eventsource.readyState === 2) {
sink.complete();
}
};
eventsource.onerror = function (error) {
sink.error(error);
};
return () => eventsource.close();
});
return new Observable((sink) =>
applySourceToSink(
makeEventStreamSource(url.toString(), this.options),
sink
)
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,10 @@ import {
createSocketIOGraphQLClient,
SocketIOGraphQLClient,
} from "@n1ru4l/socket-io-graphql-client";
import { applyAsyncIterableIteratorToSink } from "@n1ru4l/push-pull-async-iterable-iterator";
import { applyLiveQueryJSONPatch } from "@n1ru4l/graphql-live-query-patch-json-patch";
import {
ApolloClient,
InMemoryCache,
ApolloLink,
Operation,
Observable,
FetchResult,
} from "@apollo/client";
import { ApolloLink, Operation, Observable, FetchResult } from "@apollo/client";
import { print } from "graphql";
import { io } from "socket.io-client";
import { applySourceToSink } from "./shared";

class SocketIOGraphQLApolloLink extends ApolloLink {
private networkLayer: SocketIOGraphQLClient<FetchResult>;
Expand All @@ -24,14 +16,12 @@ class SocketIOGraphQLApolloLink extends ApolloLink {

public request(operation: Operation): Observable<FetchResult> | null {
return new Observable<FetchResult>((sink) =>
applyAsyncIterableIteratorToSink(
applyLiveQueryJSONPatch(
this.networkLayer.execute({
operationName: operation.operationName,
operation: print(operation.query),
variables: operation.variables,
})
),
applySourceToSink(
this.networkLayer.execute({
operationName: operation.operationName,
operation: print(operation.query),
variables: operation.variables,
}),
sink
)
);
Expand Down
15 changes: 15 additions & 0 deletions packages/todo-example/client-apollo/src/apollo-link/shared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { applyLiveQueryJSONDiffPatch } from "@n1ru4l/graphql-live-query-patch-jsondiffpatch";
import {
applyAsyncIterableIteratorToSink,
Sink,
} from "@n1ru4l/push-pull-async-iterable-iterator";

export function applySourceToSink(
source: AsyncIterableIterator<any>,
sink: Sink<any>
): () => void {
return applyAsyncIterableIteratorToSink(
applyLiveQueryJSONDiffPatch(source),
sink
);
}
3 changes: 2 additions & 1 deletion packages/todo-example/client-relay/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
"private": true,
"dependencies": {},
"devDependencies": {
"@n1ru4l/graphql-live-query-patch-json-patch": "*",
"@n1ru4l/graphql-live-query-patch-jsondiffpatch": "0.7.0",
"@n1ru4l/push-pull-async-iterable-iterator": "3.2.0",
"@n1ru4l/socket-io-graphql-client": "*",
"@repeaterjs/repeater": "3.0.4",
"@testing-library/jest-dom": "5.16.4",
"@testing-library/react": "12.1.5",
"@testing-library/user-event": "13.5.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
import { applyLiveQueryJSONPatch } from "@n1ru4l/graphql-live-query-patch-json-patch";
import { applyAsyncIterableIteratorToSink } from "@n1ru4l/push-pull-async-iterable-iterator";
import {
GraphQLResponse,
Observable,
RequestParameters,
Variables,
} from "relay-runtime";
import { Repeater } from "@repeaterjs/repeater";
import { applySourceToSink } from "./shared";

function makeEventStreamSource(url: string) {
return new Repeater<GraphQLResponse>(async (push, end) => {
const eventsource = new EventSource(url);
eventsource.onmessage = function (event) {
const data = JSON.parse(event.data);
push(data);
if (eventsource.readyState === 2) {
end();
}
};
eventsource.onerror = function (event) {
console.log("Error", event);
end(new Error("Check the console bruv."));
};
await end;

eventsource.close();
});
}

export function createHTTPFetcher(url: string) {
return (
request: RequestParameters,
Expand All @@ -28,20 +49,10 @@ export function createHTTPFetcher(url: string) {
if (variables) {
targetUrl.searchParams.append("variables", JSON.stringify(variables));
}
const eventsource = new EventSource(targetUrl.toString());

eventsource.onmessage = function (event) {
const data = JSON.parse(event.data);
sink.next(data);
if (eventsource.readyState === 2) {
sink.complete();
}
};
eventsource.onerror = function (event) {
console.log("Error", event);
sink.error(new Error("Check the console bruv."));
};
return () => eventsource.close();
return applySourceToSink(
makeEventStreamSource(targetUrl.toString()),
sink
);
}

fetch(url, {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { applyLiveQueryJSONPatch } from "@n1ru4l/graphql-live-query-patch-json-patch";
import { applyAsyncIterableIteratorToSink } from "@n1ru4l/push-pull-async-iterable-iterator";
import { createSocketIOGraphQLClient } from "@n1ru4l/socket-io-graphql-client";
import {
GraphQLResponse,
Expand All @@ -8,6 +6,7 @@ import {
Variables,
} from "relay-runtime";
import { io } from "socket.io-client";
import { applySourceToSink } from "./shared";

export function createSocketIOFetcher() {
let host =
Expand All @@ -22,14 +21,12 @@ export function createSocketIOFetcher() {
if (!request.text) throw new Error("Missing document.");
const { text: operation, name } = request;
return Observable.create<GraphQLResponse>((sink) =>
applyAsyncIterableIteratorToSink(
applyLiveQueryJSONPatch(
networkInterface.execute({
operation,
variables,
operationName: name,
})
),
applySourceToSink(
networkInterface.execute({
operation,
variables,
operationName: name,
}),
sink
)
);
Expand Down
15 changes: 15 additions & 0 deletions packages/todo-example/client-relay/src/fetcher/shared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { applyLiveQueryJSONDiffPatch } from "@n1ru4l/graphql-live-query-patch-jsondiffpatch";
import {
applyAsyncIterableIteratorToSink,
Sink,
} from "@n1ru4l/push-pull-async-iterable-iterator";

export function applySourceToSink(
source: AsyncIterableIterator<any>,
sink: Sink<any, any>
): () => void {
return applyAsyncIterableIteratorToSink(
applyLiveQueryJSONDiffPatch(source),
sink
);
}
3 changes: 2 additions & 1 deletion packages/todo-example/client-urql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"graphql": "16.0.0-experimental-stream-defer.5"
},
"devDependencies": {
"@n1ru4l/graphql-live-query-patch-json-patch": "*",
"@n1ru4l/graphql-live-query-patch-jsondiffpatch": "0.7.0",
"@repeaterjs/repeater": "3.0.4",
"@graphql-codegen/cli": "2.8.0",
"@graphql-codegen/gql-tag-operations-preset": "1.5.1",
"@n1ru4l/push-pull-async-iterable-iterator": "3.2.0",
Expand Down
58 changes: 29 additions & 29 deletions packages/todo-example/client-urql/src/urql-client/http-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,25 @@ import { Repeater } from "@repeaterjs/repeater";
import { applyLiveQueryJSONPatch } from "@n1ru4l/graphql-live-query-patch-json-patch";
import { applyAsyncIterableIteratorToSink } from "@n1ru4l/push-pull-async-iterable-iterator";
import { ExecutionLivePatchResult } from "@n1ru4l/graphql-live-query-patch";
import { applySourceToSink } from "./shared";

function makeEventStreamSource(url: string) {
return new Repeater<ExecutionLivePatchResult>(async (push, end) => {
const eventsource = new EventSource(url);
eventsource.onmessage = function (event) {
const data = JSON.parse(event.data);
push(data);
if (eventsource.readyState === 2) {
end();
}
};
eventsource.onerror = function (error) {
end(error);
};
await end;
eventsource.close();
});
}

export const createUrqlClient = (url: string) => {
return new Client({
Expand All @@ -32,38 +51,19 @@ export const createUrqlClient = (url: string) => {
return isSubscription || isLiveQuery;
},
forwardSubscription(operation) {
const create = () =>
new Repeater<ExecutionLivePatchResult>((push, stop) => {
const targetUrl = new URL(url);
targetUrl.searchParams.append("query", operation.query);
if (operation.variables) {
targetUrl.searchParams.append(
"variables",
JSON.stringify(operation.variables)
);
}
const eventsource = new EventSource(targetUrl.toString());

eventsource.onmessage = function (event) {
const data = JSON.parse(event.data);
push(data);
if (eventsource.readyState === 2) {
stop();
}
};
eventsource.onerror = function (error) {
stop(error);
};

stop.then(() => {
eventsource.close();
});
});
const targetUrl = new URL(url);
targetUrl.searchParams.append("query", operation.query);
if (operation.variables) {
targetUrl.searchParams.append(
"variables",
JSON.stringify(operation.variables)
);
}

return {
subscribe: (sink) => ({
unsubscribe: applyAsyncIterableIteratorToSink(
applyLiveQueryJSONPatch(create()),
unsubscribe: applySourceToSink(
makeEventStreamSource(targetUrl.toString()),
sink
),
}),
Expand Down
15 changes: 15 additions & 0 deletions packages/todo-example/client-urql/src/urql-client/shared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { applyLiveQueryJSONDiffPatch } from "@n1ru4l/graphql-live-query-patch-jsondiffpatch";
import {
applyAsyncIterableIteratorToSink,
Sink,
} from "@n1ru4l/push-pull-async-iterable-iterator";

export function applySourceToSink(
source: AsyncIterableIterator<any>,
sink: Sink<any>
): () => void {
return applyAsyncIterableIteratorToSink(
applyLiveQueryJSONDiffPatch(source),
sink
);
}
Loading

0 comments on commit 25ad6d0

Please sign in to comment.