Skip to content

Commit

Permalink
fix: use standard execution result for subscription forwarder closes #93
Browse files Browse the repository at this point in the history
  • Loading branch information
logaretm committed Jan 20, 2021
1 parent bf755fd commit 9ced480
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 12 deletions.
3 changes: 2 additions & 1 deletion packages/villus/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
AfterQueryCallback,
ObservableLike,
OperationWithCachePolicy,
StandardOperationResult,
} from './types';
import { VILLUS_CLIENT } from './symbols';
import { App } from 'vue-demi';
Expand Down Expand Up @@ -131,7 +132,7 @@ export class Client {
public async executeSubscription<TData = any, TVars = QueryVariables>(operation: Operation<TData, TVars>) {
const result = await this.execute<TData, TVars>(operation, 'subscription');

return (result as unknown) as ObservableLike<OperationResult<TData>>;
return (result as unknown) as ObservableLike<StandardOperationResult<TData>>;
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/villus/src/handleSubscriptions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ClientPlugin, ClientPluginOperation, ObservableLike, OperationResult } from './types';
import { ClientPlugin, ClientPluginOperation, ObservableLike, StandardOperationResult } from './types';

export type SubscriptionForwarder<TData = any> = (
operation: ClientPluginOperation
) => ObservableLike<OperationResult<TData>>;
) => ObservableLike<StandardOperationResult<TData>>;

export function handleSubscriptions(forwarder: SubscriptionForwarder): ClientPlugin {
const forward = forwarder;
Expand Down
3 changes: 3 additions & 0 deletions packages/villus/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Ref } from 'vue-demi';
import { CombinedError } from './utils/error';
import { ParsedResponse, FetchOptions, Operation } from '../../shared/src';
import type { ExecutionResult } from 'graphql';

export interface OperationResult<TData = any> {
data: TData | null;
Expand All @@ -9,6 +10,8 @@ export interface OperationResult<TData = any> {

export type CachePolicy = 'cache-and-network' | 'network-only' | 'cache-first' | 'cache-only';

export type StandardOperationResult<TData = any> = ExecutionResult<TData>;

export type QueryVariables = Record<string, any>;

export interface ObserverLike<T> {
Expand Down
28 changes: 23 additions & 5 deletions packages/villus/src/useSubscription.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ref, Ref, onMounted, unref, onBeforeUnmount, watch, isRef } from 'vue-demi';
import { VILLUS_CLIENT } from './symbols';
import { Unsub, OperationResult, QueryVariables, MaybeReactive } from './types';
import { Unsub, OperationResult, QueryVariables, MaybeReactive, StandardOperationResult } from './types';
import { CombinedError, injectWithSelf } from './utils';
import { Operation } from '../../shared/src';

Expand All @@ -26,7 +26,7 @@ export function useSubscription<TData = any, TResult = TData, TVars = QueryVaria
const isPaused = ref(false);

async function initObserver() {
function handler(result: OperationResult<TData>) {
function handleResponse(result: OperationResult<TData>) {
data.value = reduce(data.value as TResult, result) as any;
error.value = result.error;
}
Expand All @@ -39,13 +39,17 @@ export function useSubscription<TData = any, TResult = TData, TVars = QueryVaria
});

return result.subscribe({
next: handler,
next(result) {
const response = transformResult(result);

handleResponse(response);
},
// eslint-disable-next-line
complete() {},
error(err) {
const result = { data: null, error: new CombinedError({ networkError: err, response: null }) };
const response = { data: null, error: new CombinedError({ networkError: err, response: null }) };

return handler(result);
return handleResponse(response);
},
});
}
Expand Down Expand Up @@ -85,3 +89,17 @@ export function useSubscription<TData = any, TResult = TData, TVars = QueryVaria

return { data, error, isPaused, pause, resume };
}

/**
* Transforms the result from a standard operation result to villus result
*/
function transformResult<TData>(result: StandardOperationResult<TData>): OperationResult<TData> {
if (!result.errors) {
return { data: result.data || null, error: null };
}

return {
data: result.data || null,
error: new CombinedError({ graphqlErrors: [...result.errors], response: null }),
};
}
9 changes: 7 additions & 2 deletions packages/villus/test/helpers/observer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export function makeObservable(throws = false) {
export function makeObservable(throws = false, simulateError = false) {
let interval: any;
let counter = 0;
const observable = {
Expand All @@ -9,7 +9,12 @@ export function makeObservable(throws = false) {
return;
}

next({ data: { message: 'New message', id: counter++ } });
if (!simulateError) {
next({ data: { message: 'New message', id: counter++ } });
return;
}

next({ errors: [new Error('sadge')], data: null });
}, 100);

afterAll(() => {
Expand Down
31 changes: 29 additions & 2 deletions packages/villus/test/useSubscription.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/* eslint-disable no-unused-expressions */
import flushPromises from 'flush-promises';
import { mount } from './helpers/mount';
import { makeObservable } from './helpers/observer';
import { makeObservable, tick } from './helpers/observer';
import { defaultPlugins, handleSubscriptions, useClient, useSubscription } from '../src/index';
import { computed, ref } from 'vue';
import { subscribe } from 'graphql';

jest.useFakeTimers();

Expand Down Expand Up @@ -290,3 +289,31 @@ test('Fails if subscription forwarder was not set', () => {
expect(err.message).toContain('No subscription forwarder was set');
}
});

test('handles subscription errors', async () => {
mount({
setup() {
useClient({
url: 'https://test.com/graphql',
use: [handleSubscriptions(() => makeObservable(false, true)), ...defaultPlugins()],
});

const { data, error } = useSubscription<Message>({ query: `subscription { newMessages }` });

return { messages: data.value, error };
},
template: `
<div>
<div v-if="messages && !error">
<span>{{ messages.id }}</span>
</div>
<span id="error" v-if="error">{{ error }}</span>
</div>
`,
});

await flushPromises();
await tick(1);
await flushPromises();
expect(document.querySelector('#error')?.textContent).toBeTruthy();
});

0 comments on commit 9ced480

Please sign in to comment.