Skip to content

Commit

Permalink
fix(realtime/trusted-docs): Supports GraphQL subscriptions and truste…
Browse files Browse the repository at this point in the history
…d documents (#10893)

Fixes: #10892

This PR updates the SSELink to check if there is a trusted document hash
in the request. If there is, then don't also include the query.

The persisted operations plugin checks if the params has a query. If it
does then raises an error that only persisted operations are allowed.

Subscriptions failed this test because the request had both the hash and
a query. And since there were query details, the request was blocked.

Now, if there is a hash, the SSELink won't add back the query and thus
it succeeds.

---------

Co-authored-by: Josh GM Walker <[email protected]>
  • Loading branch information
dthyresson and Josh-Walker-GM authored Jul 19, 2024
1 parent d0a22f2 commit 461ca21
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 19 deletions.
11 changes: 11 additions & 0 deletions .changesets/10893.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
fix(realtime/trusted-docs): Supports GraphQL subscriptions and trusted documents (#10893) by @dthyresson

Fixes: https://github.com/redwoodjs/redwood/issues/10892

This PR updates the SSELink to check if there is a trusted document hash in the request. If there is, then don't also include the query.

The persisted operations plugin checks if the params has a query. If it does then raises an error that only persisted operations are allowed.

Subscriptions failed this test because the request had both the hash and a query. And since there were query details, the request was blocked.

Now, if there is a hash, the SSELink won't add back the query and thus it succeeds.
2 changes: 1 addition & 1 deletion packages/api/src/validations/validations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ const prepareExclusionInclusion = (
// default case sensitivity to true
const caseSensitive = Array.isArray(options)
? true
: options.caseSensitive ?? true
: (options.caseSensitive ?? true)

return caseSensitive
? [inputList, value]
Expand Down
2 changes: 1 addition & 1 deletion packages/graphql-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"@graphql-tools/merge": "9.0.4",
"@graphql-tools/schema": "10.0.3",
"@graphql-tools/utils": "10.2.0",
"@graphql-yoga/plugin-persisted-operations": "3.3.1",
"@graphql-yoga/plugin-persisted-operations": "3.5.0",
"@opentelemetry/api": "1.8.0",
"@redwoodjs/api": "workspace:*",
"@redwoodjs/context": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion packages/vite/src/buildRouteManifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export async function buildRouteManifest() {
? // @TODO(RSC_DC): this no longer resolves to anything i.e. its always null
// Because the clientBuildManifest has no pages, because all pages are Server-components?
// This may be a non-issue, because RSC pages don't need a client bundle per page (or atleast not the same bundle)
clientBuildManifest[route.relativeFilePath]?.file ?? null
(clientBuildManifest[route.relativeFilePath]?.file ?? null)
: null,
matchRegexString: route.matchRegexString,
// NOTE this is the path definition, not the actual path
Expand Down
40 changes: 30 additions & 10 deletions packages/web/src/apollo/sseLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Operation, FetchResult } from '@apollo/client/core'
import { ApolloLink } from '@apollo/client/link/core/core.cjs'
import { Observable } from '@apollo/client/utilities/utilities.cjs'
import { print } from 'graphql'
import type { ClientOptions, Client, Sink } from 'graphql-sse'
import type { ClientOptions, Client, RequestParams, Sink } from 'graphql-sse'
import { createClient } from 'graphql-sse'
interface SSELinkOptions extends Partial<ClientOptions> {
url: string
Expand Down Expand Up @@ -58,6 +58,12 @@ const mapReferrerPolicyHeader = (
}
}

// Check if the operation has a persisted query (aka trusted document)
// by checking if the operation has an `extensions` property and if it has a `persistedQuery` property.
const hasTrustedDocument = (operation: Operation) => {
return operation.extensions?.persistedQuery?.sha256Hash
}

/**
* GraphQL over Server-Sent Events (SSE) spec link for Apollo Client
*/
Expand Down Expand Up @@ -92,16 +98,30 @@ class SSELink extends ApolloLink {
})
}

public request(operation: Operation): Observable<FetchResult> {

Check warning on line 101 in packages/web/src/apollo/sseLink.ts

View workflow job for this annotation

GitHub Actions / 🦜 Publish Canary

Delete `⏎`
public request(
operation: Operation & { query?: any },
): Observable<FetchResult> {
return new Observable<FetchResult>((sink: Sink) => {
return this.client.subscribe<FetchResult>(
{ ...operation, query: print(operation.query) },
{
next: sink.next.bind(sink),
complete: sink.complete.bind(sink),
error: sink.error.bind(sink),
},
)
let request: RequestParams

// If the operation has a persisted query (aka trusted document),
// we don't need to send the query as a string.
if (hasTrustedDocument(operation)) {
delete operation.query
request = { ...operation }
} else {
request = {
...operation,
query: print(operation.query),
}
}

return this.client.subscribe<FetchResult>(request, {
next: sink.next.bind(sink),
complete: sink.complete.bind(sink),
error: sink.error.bind(sink),
})
})
}
}
Expand Down
12 changes: 6 additions & 6 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4753,14 +4753,14 @@ __metadata:
languageName: node
linkType: hard

"@graphql-yoga/plugin-persisted-operations@npm:3.3.1":
version: 3.3.1
resolution: "@graphql-yoga/plugin-persisted-operations@npm:3.3.1"
"@graphql-yoga/plugin-persisted-operations@npm:3.5.0":
version: 3.5.0
resolution: "@graphql-yoga/plugin-persisted-operations@npm:3.5.0"
peerDependencies:
"@graphql-tools/utils": ^10.0.0
graphql: ^15.2.0 || ^16.0.0
graphql-yoga: ^5.3.1
checksum: 10c0/70fa17d2eb48e194d2b3e7ebef56cf2b03327266fdd44a8812216f48a9bac6f5dd96f9f2abc9617bb4bd6e6c9e1d371350e6560bbbd6a8a96279dd114183976e
graphql-yoga: ^5.5.0
checksum: 10c0/09abfcf6fc8f8abd2c9d168d5e8d74ad5755c1b116a34e98a2f5b9c4d8343b5e28a7ac057b21c0f12cb5437f81567a92b3a98086d5354eaf43e9ebfdb621087f
languageName: node
linkType: hard

Expand Down Expand Up @@ -8205,7 +8205,7 @@ __metadata:
"@graphql-tools/merge": "npm:9.0.4"
"@graphql-tools/schema": "npm:10.0.3"
"@graphql-tools/utils": "npm:10.2.0"
"@graphql-yoga/plugin-persisted-operations": "npm:3.3.1"
"@graphql-yoga/plugin-persisted-operations": "npm:3.5.0"
"@opentelemetry/api": "npm:1.8.0"
"@redwoodjs/api": "workspace:*"
"@redwoodjs/context": "workspace:*"
Expand Down

0 comments on commit 461ca21

Please sign in to comment.