Skip to content

Instantly share code, notes, and snippets.

@ispyinternet
Last active October 1, 2020 19:25
Show Gist options
  • Save ispyinternet/05372d2963b301108c43a9447c578e43 to your computer and use it in GitHub Desktop.
Save ispyinternet/05372d2963b301108c43a9447c578e43 to your computer and use it in GitHub Desktop.
query.ts
import { pipe, fromValue, concat, map, subscribe } from 'wonka';
import { DocumentNode } from 'graphql';
import { request$, Request$ } from './request';
import { context$, Context$ } from './context';
import { source$ } from './source';
import { derived, writable, Writable, Readable } from 'svelte/store';
import { OperationContext, RequestPolicy } from '@urql/core';
export interface QueryArgs {
query: string | DocumentNode;
variables?: object;
requestPolicy?: RequestPolicy;
pollInterval?: number;
context?: Partial<OperationContext>;
pause?: boolean;
}
const initialState: Partial<ResultStore<any>> = {
fetching: false,
stale: false,
error: undefined,
data: undefined,
extensions: undefined,
operation: undefined,
};
export interface ResultStore<T> {
fetching: boolean;
stale: boolean;
error: any;
data: T;
extensions: any;
operation: any;
}
export type ExecuteQueryFunction = (args: Partial<QueryArgs>) => void;
export interface Query$<T> extends Readable<Partial<ResultStore<T>>> {
reexecute: ExecuteQueryFunction;
}
/**
* query store - readable is result of query source
* bind method allows to bind query, variables and context to template vars
* reexecuteQuery method programatically triggers a new query
*/
export function query<T>(args: QueryArgs): Query$<T> {
// create our context store
const context = {
...args.context,
...{ pollInterval: args.pollInterval, requestPolicy: args.requestPolicy },
};
const context$$: Context$ = context$(context);
// create our request store
const variables = { ...args.variables };
const request$$: Request$ = request$(args.query, variables);
// create our source store - derived by request and context
const source$$ = source$<T>(request$$, context$$);
// pause store.
const pause$: Writable<boolean> = writable(!!args.pause);
// results store.
const { subscribe, update } = writable(initialState, () => {
// subscribe to the changes store - which will trigger updates on this store
// return the unsubscribe function which should hopefully clean up!
return changes.subscribe(() => {}); // need to satify typescript.
});
// if source or pause changes then change query / state
const changes = derived(
[source$$, pause$],
([source, pause], set) => {
// for some reason typescript wants this store value to be typeof ResultState
set(initialState);
if (pause) {
update(state => ({ ...state, ...{ fetching: false, stale: false } }));
} else {
// re-run the source query and update the results store,
// return the wonka unsubscribe function - this ensures any previous wonka
// streams are unsubscribed from
return run(source, update);
}
},
initialState
);
/**
* // in the template the user can reexeute the query, for example:
* let query, nextToken, pollInterval, requestPolicy, context;
* export let numResults;
*
* // if any of the variables change, either from external or within reexecute
* $: query.reexecute({ query, variables: { nextToken, numResults }, requestPolicy, pollInterval, context })
*/
const reexecute = args => {
if (args.query) request$$.setQuery(args.query);
if (args.variables) request$$.setVars(args.variables);
if ('pause' in args) pause$.set(!!args.pause);
const context = {
...args.context,
...{
pollInterval: args.pollInterval,
requestPolicy: args.requestPolicy,
},
};
context$$.set(context);
};
return {
subscribe,
reexecute,
};
}
function run(source, update) {
return pipe(
concat([
fromValue({ fetching: true, stale: false }),
pipe(
source,
map((result: object) => ({ fetching: false, ...result }))
),
fromValue({ fetching: false, stale: false }),
]),
subscribe(result => update(state => ({ ...state, ...result })))
)[0]; // return unsubscribe function
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment