Skip to content

Commit e8e16bb

Browse files
authored
feat(core): Support Apollo Federation subscriptions multipart payloads (#3499)
1 parent f06ef34 commit e8e16bb

4 files changed

Lines changed: 75 additions & 4 deletions

File tree

.changeset/small-cheetahs-jam.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@urql/core': minor
3+
---
4+
5+
Support [Apollo Federation's format](https://www.apollographql.com/docs/router/executing-operations/subscription-multipart-protocol/) for subscription results in `multipart/mixed` responses (result properties essentially are namespaced on a `payload` key)

packages/core/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ export interface ExecutionResult {
223223
* @see {@link https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md#payload-format} for the DeferStream spec
224224
*/
225225
hasNext?: boolean;
226+
payload?: Omit<ExecutionResult, 'payload'>;
226227
}
227228

228229
/** A source of {@link OperationResult | OperationResults}, convertable to a promise, subscribable, or Wonka Source.

packages/core/src/utils/result.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { describe, it, expect } from 'vitest';
22
import { OperationResult } from '../types';
33
import { queryOperation, subscriptionOperation } from '../test-utils';
44
import { makeResult, mergeResultPatch } from './result';
5+
import { GraphQLError } from '@0no-co/graphql.web';
6+
import { CombinedError } from './error';
57

68
describe('makeResult', () => {
79
it('adds extensions and errors correctly', () => {
@@ -180,6 +182,62 @@ describe('mergeResultPatch (defer/stream pre June-2023)', () => {
180182
expect(merged.hasNext).toBe(true);
181183
});
182184

185+
it('should work with the payload property', () => {
186+
const prevResult: OperationResult = {
187+
operation: subscriptionOperation,
188+
data: {
189+
__typename: 'Subscription',
190+
event: 1,
191+
},
192+
stale: false,
193+
hasNext: true,
194+
};
195+
196+
const merged = mergeResultPatch(prevResult, {
197+
payload: {
198+
data: {
199+
__typename: 'Subscription',
200+
event: 2,
201+
},
202+
},
203+
});
204+
205+
expect(merged.data).not.toBe(prevResult.data);
206+
expect(merged.data.event).toBe(2);
207+
expect(merged.hasNext).toBe(true);
208+
});
209+
210+
it('should work with the payload property and errors', () => {
211+
const prevResult: OperationResult = {
212+
operation: subscriptionOperation,
213+
data: {
214+
__typename: 'Subscription',
215+
event: 1,
216+
},
217+
stale: false,
218+
hasNext: true,
219+
};
220+
221+
const merged = mergeResultPatch(prevResult, {
222+
payload: {
223+
data: {
224+
__typename: 'Subscription',
225+
event: 2,
226+
},
227+
},
228+
errors: [new GraphQLError('Something went horribly wrong')],
229+
});
230+
231+
expect(merged.data).not.toBe(prevResult.data);
232+
expect(merged.data.event).toBe(2);
233+
expect(merged.error).toEqual(
234+
new CombinedError({
235+
graphQLErrors: [new GraphQLError('Something went horribly wrong')],
236+
})
237+
);
238+
expect(merged.hasNext).toBe(true);
239+
});
240+
183241
it('should ignore invalid patches', () => {
184242
const prevResult: OperationResult = {
185243
operation: queryOperation,

packages/core/src/utils/result.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,12 @@ export const mergeResultPatch = (
9292
pending?: ExecutionResult['pending']
9393
): OperationResult => {
9494
let errors = prevResult.error ? prevResult.error.graphQLErrors : [];
95-
let hasExtensions = !!prevResult.extensions || !!nextResult.extensions;
96-
const extensions = { ...prevResult.extensions, ...nextResult.extensions };
95+
let hasExtensions =
96+
!!prevResult.extensions || !!(nextResult.payload || nextResult).extensions;
97+
const extensions = {
98+
...prevResult.extensions,
99+
...(nextResult.payload || nextResult).extensions,
100+
};
97101

98102
let incremental = nextResult.incremental;
99103

@@ -146,8 +150,11 @@ export const mergeResultPatch = (
146150
}
147151
}
148152
} else {
149-
withData.data = nextResult.data || prevResult.data;
150-
errors = (nextResult.errors as any[]) || errors;
153+
withData.data = (nextResult.payload || nextResult).data || prevResult.data;
154+
errors =
155+
(nextResult.errors as any[]) ||
156+
(nextResult.payload && nextResult.payload.errors) ||
157+
errors;
151158
}
152159

153160
return {

0 commit comments

Comments
 (0)