Skip to content

Commit

Permalink
feat(core): Support Apollo Federation subscriptions multipart payloads (
Browse files Browse the repository at this point in the history
  • Loading branch information
JoviDeCroock authored Mar 2, 2024
1 parent f06ef34 commit e8e16bb
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/small-cheetahs-jam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@urql/core': minor
---

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)
1 change: 1 addition & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ export interface ExecutionResult {
* @see {@link https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md#payload-format} for the DeferStream spec
*/
hasNext?: boolean;
payload?: Omit<ExecutionResult, 'payload'>;
}

/** A source of {@link OperationResult | OperationResults}, convertable to a promise, subscribable, or Wonka Source.
Expand Down
58 changes: 58 additions & 0 deletions packages/core/src/utils/result.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { describe, it, expect } from 'vitest';
import { OperationResult } from '../types';
import { queryOperation, subscriptionOperation } from '../test-utils';
import { makeResult, mergeResultPatch } from './result';
import { GraphQLError } from '@0no-co/graphql.web';
import { CombinedError } from './error';

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

it('should work with the payload property', () => {
const prevResult: OperationResult = {
operation: subscriptionOperation,
data: {
__typename: 'Subscription',
event: 1,
},
stale: false,
hasNext: true,
};

const merged = mergeResultPatch(prevResult, {
payload: {
data: {
__typename: 'Subscription',
event: 2,
},
},
});

expect(merged.data).not.toBe(prevResult.data);
expect(merged.data.event).toBe(2);
expect(merged.hasNext).toBe(true);
});

it('should work with the payload property and errors', () => {
const prevResult: OperationResult = {
operation: subscriptionOperation,
data: {
__typename: 'Subscription',
event: 1,
},
stale: false,
hasNext: true,
};

const merged = mergeResultPatch(prevResult, {
payload: {
data: {
__typename: 'Subscription',
event: 2,
},
},
errors: [new GraphQLError('Something went horribly wrong')],
});

expect(merged.data).not.toBe(prevResult.data);
expect(merged.data.event).toBe(2);
expect(merged.error).toEqual(
new CombinedError({
graphQLErrors: [new GraphQLError('Something went horribly wrong')],
})
);
expect(merged.hasNext).toBe(true);
});

it('should ignore invalid patches', () => {
const prevResult: OperationResult = {
operation: queryOperation,
Expand Down
15 changes: 11 additions & 4 deletions packages/core/src/utils/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,12 @@ export const mergeResultPatch = (
pending?: ExecutionResult['pending']
): OperationResult => {
let errors = prevResult.error ? prevResult.error.graphQLErrors : [];
let hasExtensions = !!prevResult.extensions || !!nextResult.extensions;
const extensions = { ...prevResult.extensions, ...nextResult.extensions };
let hasExtensions =
!!prevResult.extensions || !!(nextResult.payload || nextResult).extensions;
const extensions = {
...prevResult.extensions,
...(nextResult.payload || nextResult).extensions,
};

let incremental = nextResult.incremental;

Expand Down Expand Up @@ -146,8 +150,11 @@ export const mergeResultPatch = (
}
}
} else {
withData.data = nextResult.data || prevResult.data;
errors = (nextResult.errors as any[]) || errors;
withData.data = (nextResult.payload || nextResult).data || prevResult.data;
errors =
(nextResult.errors as any[]) ||
(nextResult.payload && nextResult.payload.errors) ||
errors;
}

return {
Expand Down

0 comments on commit e8e16bb

Please sign in to comment.