-
Notifications
You must be signed in to change notification settings - Fork 565
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Thin Client Integration packages (#2066)
* Thin Client Integration packages * 🤦 * ..
- Loading branch information
Showing
10 changed files
with
74 additions
and
334 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
'@graphql-yoga/apollo-link': patch | ||
'@graphql-yoga/urql-exchange': patch | ||
--- | ||
|
||
Thinner Client integration packages |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,62 +1,13 @@ | ||
import * as apolloImport from '@apollo/client' | ||
import { ExecutorLink } from '@graphql-tools/executor-apollo-link' | ||
import { | ||
LoadFromUrlOptions, | ||
SubscriptionProtocol, | ||
UrlLoader, | ||
} from '@graphql-tools/url-loader' | ||
import { ExecutionRequest, isAsyncIterable } from '@graphql-tools/utils' | ||
HTTPExecutorOptions, | ||
buildHTTPExecutor, | ||
} from '@graphql-tools/executor-http' | ||
|
||
export type YogaLinkOptions = LoadFromUrlOptions & { endpoint: string } | ||
export type YogaLinkOptions = HTTPExecutorOptions | ||
|
||
const apollo: typeof apolloImport = | ||
(apolloImport as any)?.default ?? apolloImport | ||
|
||
function createYogaApolloRequestHandler( | ||
options: YogaLinkOptions, | ||
): apolloImport.RequestHandler { | ||
const urlLoader = new UrlLoader() | ||
const executor = urlLoader.getExecutorAsync(options.endpoint, { | ||
subscriptionsProtocol: SubscriptionProtocol.SSE, | ||
multipart: true, | ||
...options, | ||
}) | ||
return function graphQLYogaApolloRequestHandler( | ||
operation: apolloImport.Operation, | ||
): apolloImport.Observable<apolloImport.FetchResult> { | ||
return new apollo.Observable((observer) => { | ||
const executionRequest: ExecutionRequest = { | ||
document: operation.query, | ||
variables: operation.variables, | ||
operationName: operation.operationName, | ||
extensions: operation.extensions, | ||
context: operation.getContext(), | ||
} | ||
executor(executionRequest) | ||
.then(async (results) => { | ||
if (isAsyncIterable(results)) { | ||
for await (const result of results) { | ||
if (observer.closed) { | ||
return | ||
} | ||
observer.next(result) | ||
} | ||
observer.complete() | ||
} else if (!observer.closed) { | ||
observer.next(results) | ||
observer.complete() | ||
} | ||
}) | ||
.catch((error) => { | ||
if (!observer.closed) { | ||
observer.error(error) | ||
} | ||
}) | ||
}) | ||
} | ||
} | ||
|
||
export class YogaLink extends apollo.ApolloLink { | ||
export class YogaLink extends ExecutorLink { | ||
constructor(options: YogaLinkOptions) { | ||
super(createYogaApolloRequestHandler(options)) | ||
super(buildHTTPExecutor(options as any)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,145 +1,12 @@ | ||
import { | ||
Source, | ||
pipe, | ||
share, | ||
filter, | ||
takeUntil, | ||
mergeMap, | ||
merge, | ||
make, | ||
} from 'wonka' | ||
buildHTTPExecutor, | ||
HTTPExecutorOptions, | ||
} from '@graphql-tools/executor-http' | ||
import { executorExchange } from '@graphql-tools/executor-urql-exchange' | ||
import { Exchange } from '@urql/core' | ||
|
||
import { | ||
Exchange, | ||
ExecutionResult, | ||
makeResult, | ||
makeErrorResult, | ||
mergeResultPatch, | ||
Operation, | ||
OperationResult, | ||
getOperationName, | ||
OperationContext, | ||
ExchangeIO, | ||
AnyVariables, | ||
} from '@urql/core' | ||
|
||
import { ExecutionRequest, isAsyncIterable } from '@graphql-tools/utils' | ||
import { | ||
LoadFromUrlOptions, | ||
SubscriptionProtocol, | ||
UrlLoader, | ||
} from '@graphql-tools/url-loader' | ||
import { OperationTypeNode } from 'graphql' | ||
|
||
export type YogaExchangeOptions = LoadFromUrlOptions | ||
|
||
export function yogaExchange(options?: YogaExchangeOptions): Exchange { | ||
const urlLoader = new UrlLoader() | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function makeYogaSource<TData extends Record<string, any>>( | ||
operation: Operation<TData>, | ||
): Source<OperationResult<TData>> { | ||
const operationName = getOperationName(operation.query) | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const executionRequest: ExecutionRequest<any, OperationContext> = { | ||
document: operation.query, | ||
operationName, | ||
operationType: operation.kind as OperationTypeNode, | ||
variables: operation.variables, | ||
context: operation.context, | ||
extensions: { | ||
endpoint: operation.context.url, | ||
headers: operation.context.headers, | ||
}, | ||
} | ||
const extraFetchOptions = | ||
typeof operation.context.fetchOptions === 'function' | ||
? operation.context.fetchOptions() | ||
: operation.context.fetchOptions | ||
const executor = urlLoader.getExecutorAsync( | ||
options?.endpoint || operation.context.url, | ||
{ | ||
subscriptionsProtocol: SubscriptionProtocol.SSE, | ||
multipart: true, | ||
customFetch: operation.context.fetch, | ||
useGETForQueries: !!operation.context.preferGetMethod, | ||
headers: extraFetchOptions?.headers as Record<string, string>, | ||
method: extraFetchOptions?.method as 'GET' | 'POST', | ||
credentials: extraFetchOptions?.credentials, | ||
...options, | ||
}, | ||
) | ||
return make<OperationResult<TData>>((observer) => { | ||
let ended = false | ||
executor(executionRequest) | ||
.then( | ||
async (result: ExecutionResult | AsyncIterable<ExecutionResult>) => { | ||
if (ended || !result) { | ||
return | ||
} | ||
if (!isAsyncIterable(result)) { | ||
observer.next(makeResult(operation, result)) | ||
} else { | ||
let prevResult: OperationResult<TData, AnyVariables> | null = null | ||
|
||
for await (const value of result) { | ||
if (value) { | ||
prevResult = prevResult | ||
? mergeResultPatch(prevResult, value) | ||
: makeResult(operation, value) | ||
observer.next(prevResult) | ||
} | ||
if (ended) { | ||
break | ||
} | ||
} | ||
} | ||
observer.complete() | ||
}, | ||
) | ||
.catch((error) => { | ||
observer.next(makeErrorResult(operation, error)) | ||
}) | ||
.finally(() => { | ||
ended = true | ||
observer.complete() | ||
}) | ||
return () => { | ||
ended = true | ||
} | ||
}) | ||
} | ||
return function yogaExchangeFn({ forward }): ExchangeIO { | ||
return function yogaExchangeIO<TData, TVariables extends AnyVariables>( | ||
ops$: Source<Operation<TData, TVariables>>, | ||
): Source<OperationResult<TData>> { | ||
const sharedOps$ = share(ops$) | ||
|
||
const executedOps$ = pipe( | ||
sharedOps$, | ||
filter( | ||
(operation) => | ||
operation.kind === 'query' || | ||
operation.kind === 'mutation' || | ||
operation.kind === 'subscription', | ||
), | ||
mergeMap((operation) => { | ||
const teardown$ = pipe( | ||
sharedOps$, | ||
filter((op) => op.kind === 'teardown' && op.key === operation.key), | ||
) | ||
|
||
return pipe(makeYogaSource(operation), takeUntil(teardown$)) | ||
}), | ||
) | ||
|
||
const forwardedOps$ = pipe( | ||
sharedOps$, | ||
filter((operation) => operation.kind === 'teardown'), | ||
forward, | ||
) | ||
export type YogaExchangeOptions = HTTPExecutorOptions | ||
|
||
return merge([executedOps$, forwardedOps$]) | ||
} | ||
} | ||
export function yogaExchange(options?: HTTPExecutorOptions): Exchange { | ||
return executorExchange(buildHTTPExecutor(options as any)) | ||
} |
Oops, something went wrong.