@makerx/graphql-core 3.0.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -0
- package/package.json +12 -1
- package/testing/index.d.cts +1 -0
- package/testing/index.d.mts +1 -0
- package/testing/index.d.ts +1 -0
- package/testing/index.js +8 -0
- package/testing/index.js.map +1 -0
- package/testing/index.mjs +2 -0
- package/testing/index.mjs.map +1 -0
- package/testing/subscribe-operation.d.cts +31 -0
- package/testing/subscribe-operation.d.mts +31 -0
- package/testing/subscribe-operation.d.ts +31 -0
- package/testing/subscribe-operation.js +43 -0
- package/testing/subscribe-operation.js.map +1 -0
- package/testing/subscribe-operation.mjs +41 -0
- package/testing/subscribe-operation.mjs.map +1 -0
package/README.md
CHANGED
|
@@ -405,6 +405,104 @@ The returned value is both an `AsyncIterator` and `AsyncIterable`, so it can be
|
|
|
405
405
|
extractAnonymousOperationName('subscription { onUpdate { id } }') // 'subscription onUpdate'
|
|
406
406
|
```
|
|
407
407
|
|
|
408
|
+
## GraphQL test helpers
|
|
409
|
+
|
|
410
|
+
The `@makerx/graphql-core/testing` submodule provides helpers for running GraphQL operations directly against a schema, without spinning up an HTTP or websocket server.
|
|
411
|
+
|
|
412
|
+
Bypassing the transport layer supports complete control over JWT payloads and other operation context inputs required to set up complex test scenarios.
|
|
413
|
+
|
|
414
|
+
This complements the `@makerx/graphql-apollo-server/testing` module, which exports `buildExecuteOperation` for running queries and mutations directly against an `ApolloServer` instance. Subscriptions normally flow over websockets via [graphql-ws](https://the-guild.dev/graphql/ws), so they need their own helper that calls `graphql.subscribe` directly against the schema.
|
|
415
|
+
|
|
416
|
+
### buildSubscribeOperation
|
|
417
|
+
|
|
418
|
+
`buildSubscribeOperation` accepts a `GraphQLSchema` (or async schema factory) and a context creation function and returns a `subscribeOperation` function which:
|
|
419
|
+
|
|
420
|
+
- is strongly typed to the GraphQL context
|
|
421
|
+
- accepts `TypedDocumentNode` subscriptions to provide strong operation typing
|
|
422
|
+
- forwards any additional arguments to the supplied context creation function
|
|
423
|
+
- returns the subscription's `AsyncIterableIterator<ExecutionResult<TData>>` so tests can consume events via `for await` or manual `next()` calls
|
|
424
|
+
- throws if the subscription fails to produce an iterator (e.g. a validation error), surfacing the underlying errors
|
|
425
|
+
|
|
426
|
+
The shape of the context creation function — and the JWT/user factories used to drive it — depends on your GraphQL implementation, so the example below illustrates one common pattern using [Vitest test contexts](https://vitest.dev/guide/test-context), matching the [`buildExecuteOperation`](https://github.com/MakerXStudio/graphql-apollo-server#apollo-server-test-helpers) examples for queries and mutations.
|
|
427
|
+
|
|
428
|
+
#### Vitest GraphQL context example
|
|
429
|
+
|
|
430
|
+
Note: extend auth context (e.g. `buildJwt`, `buildUserJwt`) and other context as required — see the [graphql-apollo-server test helper docs](https://github.com/MakerXStudio/graphql-apollo-server#vitest-auth-context-example) for a complete auth fixture example.
|
|
431
|
+
|
|
432
|
+
`test/graphql.ts`
|
|
433
|
+
|
|
434
|
+
```ts
|
|
435
|
+
import { buildSubscribeOperation } from '@makerx/graphql-core/testing'
|
|
436
|
+
import { test as baseTest } from './auth'
|
|
437
|
+
|
|
438
|
+
const createContext = async (jwtPayload?: JwtPayload): Promise<GraphQLContext> => {
|
|
439
|
+
const user = await findUpdateOrCreateUser(jwtPayload, randomUUID())
|
|
440
|
+
const baseContext: BaseContext = { user, logger, requestInfo, started: Date.now() }
|
|
441
|
+
const extraContext = await augmentContext(baseContext)
|
|
442
|
+
return { ...baseContext, ...extraContext }
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export const test = baseTest
|
|
446
|
+
.extend('schema', { scope: 'worker' }, async ({}) => createSchema())
|
|
447
|
+
.extend('backgroundJobs', { scope: 'worker' }, backgroundJobsFixture)
|
|
448
|
+
.extend('executeOperation', { scope: 'worker' }, async ({ schema }, { onCleanup }) => {
|
|
449
|
+
const server = new ApolloServer<GraphQLContext>({ schema })
|
|
450
|
+
onCleanup(() => server.stop())
|
|
451
|
+
return buildExecuteOperation(server, createContext)
|
|
452
|
+
})
|
|
453
|
+
.extend('subscribeOperation', { scope: 'worker' }, async ({ schema }) => {
|
|
454
|
+
return buildSubscribeOperation(schema, createContext)
|
|
455
|
+
})
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
#### job-status.subscription.test.ts
|
|
459
|
+
|
|
460
|
+
The test below uses the `graphql` template-literal tag from [GraphQL-Codegen](https://the-guild.dev/graphql/codegen/docs/getting-started) to produce strongly typed operations.
|
|
461
|
+
|
|
462
|
+
```ts
|
|
463
|
+
import { describe, expect } from 'vitest'
|
|
464
|
+
import { graphql } from './gql'
|
|
465
|
+
import { test } from '../../../test/graphql'
|
|
466
|
+
|
|
467
|
+
const jobStatusSubscription = graphql(`
|
|
468
|
+
subscription JobStatus($jobId: ID!) {
|
|
469
|
+
jobStatus(jobId: $jobId) {
|
|
470
|
+
jobId
|
|
471
|
+
status
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
`)
|
|
475
|
+
|
|
476
|
+
describe('jobStatus subscription operation', () => {
|
|
477
|
+
test('anonymous subscribes fail', async ({ subscribeOperation }) => {
|
|
478
|
+
await expect(subscribeOperation({ subscription: jobStatusSubscription, variables: { jobId: 'job-1' } })).rejects.toThrow(
|
|
479
|
+
/Not authenticated/,
|
|
480
|
+
)
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
test('authenticated subscribers receive events until terminal status', async ({ subscribeOperation, buildUserJwt }) => {
|
|
484
|
+
const jwt = buildUserJwt()
|
|
485
|
+
const jobId = randomUUID()
|
|
486
|
+
|
|
487
|
+
const iterator = await subscribeOperation({ subscription: jobStatusSubscription, variables: { jobId } }, jwt)
|
|
488
|
+
|
|
489
|
+
// app-specific: trigger the events your subscription resolves over
|
|
490
|
+
// await addJob('demo', { demoPayload: 'subscription-test' }, { jobId })
|
|
491
|
+
|
|
492
|
+
const events: JobStatusSubscription[] = []
|
|
493
|
+
for await (const result of iterator) {
|
|
494
|
+
if (result.data) events.push(result.data)
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
expect(events.at(-1)?.jobStatus.status).toBe('Completed')
|
|
498
|
+
})
|
|
499
|
+
})
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
The first argument is a `TypedSubscribeRequest` (`subscription`, `variables`, `operationName`); any further arguments are forwarded verbatim to the context creation function — in the example above, the optional `JwtPayload` produced by `buildUserJwt()`.
|
|
503
|
+
|
|
504
|
+
When pairing this helper with [`wrapSubscriptionIterator`](#wrapsubscriptioniterator), `eventIsFinal` will close the subscription once a terminal event arrives, ending the `for await` loop above without manual cleanup.
|
|
505
|
+
|
|
408
506
|
## \*Express peer dependency
|
|
409
507
|
|
|
410
508
|
ApolloServer v3 standardises on the Express request representation.
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"**"
|
|
5
5
|
],
|
|
6
6
|
"name": "@makerx/graphql-core",
|
|
7
|
-
"version": "3.0
|
|
7
|
+
"version": "3.1.0",
|
|
8
8
|
"private": false,
|
|
9
9
|
"description": "A set of core GraphQL utilities that MakerX uses to build GraphQL APIs",
|
|
10
10
|
"author": "MakerX",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"url": "git+https://github.com/MakerXStudio/graphql-core.git"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
|
+
"@graphql-typed-document-node/core": ">=3.2.0",
|
|
24
25
|
"@makerx/node-common": ">=1",
|
|
25
26
|
"es-toolkit": ">=1",
|
|
26
27
|
"express": ">=4",
|
|
@@ -74,6 +75,16 @@
|
|
|
74
75
|
"types": "./subscriptions/index.d.cts",
|
|
75
76
|
"default": "./subscriptions/index.js"
|
|
76
77
|
}
|
|
78
|
+
},
|
|
79
|
+
"./testing": {
|
|
80
|
+
"import": {
|
|
81
|
+
"types": "./testing/index.d.mts",
|
|
82
|
+
"default": "./testing/index.mjs"
|
|
83
|
+
},
|
|
84
|
+
"require": {
|
|
85
|
+
"types": "./testing/index.d.cts",
|
|
86
|
+
"default": "./testing/index.js"
|
|
87
|
+
}
|
|
77
88
|
}
|
|
78
89
|
}
|
|
79
90
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './subscribe-operation';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './subscribe-operation.mjs';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './subscribe-operation';
|
package/testing/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
2
|
+
import { type DocumentNode, type ExecutionResult, type GraphQLSchema } from 'graphql';
|
|
3
|
+
import type { AnyGraphqlContext } from '../context';
|
|
4
|
+
export type VariableValues = {
|
|
5
|
+
[key: string]: any;
|
|
6
|
+
};
|
|
7
|
+
export type TypedSubscribeRequest<TData = Record<string, unknown>, TVariables extends VariableValues = VariableValues> = {
|
|
8
|
+
subscription: string | DocumentNode | TypedDocumentNode<TData, TVariables>;
|
|
9
|
+
variables?: TVariables;
|
|
10
|
+
operationName?: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Returns a `subscribeOperation` test helper for the provided schema and context creation function.
|
|
14
|
+
*
|
|
15
|
+
* The returned function runs a subscription operation directly against the schema using `graphql.subscribe`,
|
|
16
|
+
* bypassing the websocket / `graphql-ws` transport. This gives tests complete control over JWT payloads
|
|
17
|
+
* and other context inputs, the same way `buildExecuteOperation` does for queries and mutations in
|
|
18
|
+
* `@makerx/graphql-apollo-server/testing`.
|
|
19
|
+
*
|
|
20
|
+
* The returned function:
|
|
21
|
+
* - is strongly typed to the GraphQL context
|
|
22
|
+
* - accepts `TypedDocumentNode` subscriptions to provide strong operation typing
|
|
23
|
+
* - forwards any additional arguments to the supplied context creation function
|
|
24
|
+
* - returns the subscription's `AsyncIterableIterator<ExecutionResult<TData>>` so tests can consume events via `for await` or manual `next()` calls
|
|
25
|
+
* - throws if the subscription fails to produce an iterator (e.g. a validation error), surfacing the underlying errors
|
|
26
|
+
*
|
|
27
|
+
* @param schema The executable `GraphQLSchema`, or a factory that resolves one (useful when schema construction is async).
|
|
28
|
+
* @param createContext A context creation function. Any args supplied after the request are forwarded to it,
|
|
29
|
+
* so test fixtures can drive auth/user state per-call (e.g. `(jwtPayload?) => Promise<GraphQLContext>`).
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildSubscribeOperation<TContext extends AnyGraphqlContext, TContextFunction extends (...args: any) => Promise<TContext>>(schema: GraphQLSchema | (() => Promise<GraphQLSchema>), createContext: TContextFunction): <TData = Record<string, unknown>, TVariables extends VariableValues = VariableValues>({ subscription, variables, operationName }: TypedSubscribeRequest<TData, TVariables>, ...createContextArgs: Parameters<TContextFunction>) => Promise<AsyncIterableIterator<ExecutionResult<TData>>>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
2
|
+
import { type DocumentNode, type ExecutionResult, type GraphQLSchema } from 'graphql';
|
|
3
|
+
import type { AnyGraphqlContext } from '../context.mjs';
|
|
4
|
+
export type VariableValues = {
|
|
5
|
+
[key: string]: any;
|
|
6
|
+
};
|
|
7
|
+
export type TypedSubscribeRequest<TData = Record<string, unknown>, TVariables extends VariableValues = VariableValues> = {
|
|
8
|
+
subscription: string | DocumentNode | TypedDocumentNode<TData, TVariables>;
|
|
9
|
+
variables?: TVariables;
|
|
10
|
+
operationName?: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Returns a `subscribeOperation` test helper for the provided schema and context creation function.
|
|
14
|
+
*
|
|
15
|
+
* The returned function runs a subscription operation directly against the schema using `graphql.subscribe`,
|
|
16
|
+
* bypassing the websocket / `graphql-ws` transport. This gives tests complete control over JWT payloads
|
|
17
|
+
* and other context inputs, the same way `buildExecuteOperation` does for queries and mutations in
|
|
18
|
+
* `@makerx/graphql-apollo-server/testing`.
|
|
19
|
+
*
|
|
20
|
+
* The returned function:
|
|
21
|
+
* - is strongly typed to the GraphQL context
|
|
22
|
+
* - accepts `TypedDocumentNode` subscriptions to provide strong operation typing
|
|
23
|
+
* - forwards any additional arguments to the supplied context creation function
|
|
24
|
+
* - returns the subscription's `AsyncIterableIterator<ExecutionResult<TData>>` so tests can consume events via `for await` or manual `next()` calls
|
|
25
|
+
* - throws if the subscription fails to produce an iterator (e.g. a validation error), surfacing the underlying errors
|
|
26
|
+
*
|
|
27
|
+
* @param schema The executable `GraphQLSchema`, or a factory that resolves one (useful when schema construction is async).
|
|
28
|
+
* @param createContext A context creation function. Any args supplied after the request are forwarded to it,
|
|
29
|
+
* so test fixtures can drive auth/user state per-call (e.g. `(jwtPayload?) => Promise<GraphQLContext>`).
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildSubscribeOperation<TContext extends AnyGraphqlContext, TContextFunction extends (...args: any) => Promise<TContext>>(schema: GraphQLSchema | (() => Promise<GraphQLSchema>), createContext: TContextFunction): <TData = Record<string, unknown>, TVariables extends VariableValues = VariableValues>({ subscription, variables, operationName }: TypedSubscribeRequest<TData, TVariables>, ...createContextArgs: Parameters<TContextFunction>) => Promise<AsyncIterableIterator<ExecutionResult<TData>>>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
2
|
+
import { type DocumentNode, type ExecutionResult, type GraphQLSchema } from 'graphql';
|
|
3
|
+
import type { AnyGraphqlContext } from '../context';
|
|
4
|
+
export type VariableValues = {
|
|
5
|
+
[key: string]: any;
|
|
6
|
+
};
|
|
7
|
+
export type TypedSubscribeRequest<TData = Record<string, unknown>, TVariables extends VariableValues = VariableValues> = {
|
|
8
|
+
subscription: string | DocumentNode | TypedDocumentNode<TData, TVariables>;
|
|
9
|
+
variables?: TVariables;
|
|
10
|
+
operationName?: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Returns a `subscribeOperation` test helper for the provided schema and context creation function.
|
|
14
|
+
*
|
|
15
|
+
* The returned function runs a subscription operation directly against the schema using `graphql.subscribe`,
|
|
16
|
+
* bypassing the websocket / `graphql-ws` transport. This gives tests complete control over JWT payloads
|
|
17
|
+
* and other context inputs, the same way `buildExecuteOperation` does for queries and mutations in
|
|
18
|
+
* `@makerx/graphql-apollo-server/testing`.
|
|
19
|
+
*
|
|
20
|
+
* The returned function:
|
|
21
|
+
* - is strongly typed to the GraphQL context
|
|
22
|
+
* - accepts `TypedDocumentNode` subscriptions to provide strong operation typing
|
|
23
|
+
* - forwards any additional arguments to the supplied context creation function
|
|
24
|
+
* - returns the subscription's `AsyncIterableIterator<ExecutionResult<TData>>` so tests can consume events via `for await` or manual `next()` calls
|
|
25
|
+
* - throws if the subscription fails to produce an iterator (e.g. a validation error), surfacing the underlying errors
|
|
26
|
+
*
|
|
27
|
+
* @param schema The executable `GraphQLSchema`, or a factory that resolves one (useful when schema construction is async).
|
|
28
|
+
* @param createContext A context creation function. Any args supplied after the request are forwarded to it,
|
|
29
|
+
* so test fixtures can drive auth/user state per-call (e.g. `(jwtPayload?) => Promise<GraphQLContext>`).
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildSubscribeOperation<TContext extends AnyGraphqlContext, TContextFunction extends (...args: any) => Promise<TContext>>(schema: GraphQLSchema | (() => Promise<GraphQLSchema>), createContext: TContextFunction): <TData = Record<string, unknown>, TVariables extends VariableValues = VariableValues>({ subscription, variables, operationName }: TypedSubscribeRequest<TData, TVariables>, ...createContextArgs: Parameters<TContextFunction>) => Promise<AsyncIterableIterator<ExecutionResult<TData>>>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var graphql = require('graphql');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns a `subscribeOperation` test helper for the provided schema and context creation function.
|
|
7
|
+
*
|
|
8
|
+
* The returned function runs a subscription operation directly against the schema using `graphql.subscribe`,
|
|
9
|
+
* bypassing the websocket / `graphql-ws` transport. This gives tests complete control over JWT payloads
|
|
10
|
+
* and other context inputs, the same way `buildExecuteOperation` does for queries and mutations in
|
|
11
|
+
* `@makerx/graphql-apollo-server/testing`.
|
|
12
|
+
*
|
|
13
|
+
* The returned function:
|
|
14
|
+
* - is strongly typed to the GraphQL context
|
|
15
|
+
* - accepts `TypedDocumentNode` subscriptions to provide strong operation typing
|
|
16
|
+
* - forwards any additional arguments to the supplied context creation function
|
|
17
|
+
* - returns the subscription's `AsyncIterableIterator<ExecutionResult<TData>>` so tests can consume events via `for await` or manual `next()` calls
|
|
18
|
+
* - throws if the subscription fails to produce an iterator (e.g. a validation error), surfacing the underlying errors
|
|
19
|
+
*
|
|
20
|
+
* @param schema The executable `GraphQLSchema`, or a factory that resolves one (useful when schema construction is async).
|
|
21
|
+
* @param createContext A context creation function. Any args supplied after the request are forwarded to it,
|
|
22
|
+
* so test fixtures can drive auth/user state per-call (e.g. `(jwtPayload?) => Promise<GraphQLContext>`).
|
|
23
|
+
*/
|
|
24
|
+
function buildSubscribeOperation(schema, createContext) {
|
|
25
|
+
return async function subscribeOperation({ subscription, variables, operationName }, ...createContextArgs) {
|
|
26
|
+
const resolvedSchema = typeof schema === 'function' ? await schema() : schema;
|
|
27
|
+
const document = typeof subscription === 'string' ? graphql.parse(subscription) : subscription;
|
|
28
|
+
const contextValue = await createContext(...createContextArgs);
|
|
29
|
+
const result = await graphql.subscribe({
|
|
30
|
+
schema: resolvedSchema,
|
|
31
|
+
document,
|
|
32
|
+
contextValue,
|
|
33
|
+
variableValues: variables,
|
|
34
|
+
operationName,
|
|
35
|
+
});
|
|
36
|
+
if (Symbol.asyncIterator in result)
|
|
37
|
+
return result;
|
|
38
|
+
throw new Error(`Subscription did not produce an iterator: ${JSON.stringify(result.errors)}`);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
exports.buildSubscribeOperation = buildSubscribeOperation;
|
|
43
|
+
//# sourceMappingURL=subscribe-operation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscribe-operation.js","sources":["../../src/testing/subscribe-operation.ts"],"sourcesContent":["import type { TypedDocumentNode } from '@graphql-typed-document-node/core'\nimport { parse, subscribe, type DocumentNode, type ExecutionResult, type GraphQLSchema } from 'graphql'\nimport type { AnyGraphqlContext } from '../context'\n\nexport type VariableValues = { [key: string]: any }\n\nexport type TypedSubscribeRequest<TData = Record<string, unknown>, TVariables extends VariableValues = VariableValues> = {\n subscription: string | DocumentNode | TypedDocumentNode<TData, TVariables>\n variables?: TVariables\n operationName?: string\n}\n\n/**\n * Returns a `subscribeOperation` test helper for the provided schema and context creation function.\n *\n * The returned function runs a subscription operation directly against the schema using `graphql.subscribe`,\n * bypassing the websocket / `graphql-ws` transport. This gives tests complete control over JWT payloads\n * and other context inputs, the same way `buildExecuteOperation` does for queries and mutations in\n * `@makerx/graphql-apollo-server/testing`.\n *\n * The returned function:\n * - is strongly typed to the GraphQL context\n * - accepts `TypedDocumentNode` subscriptions to provide strong operation typing\n * - forwards any additional arguments to the supplied context creation function\n * - returns the subscription's `AsyncIterableIterator<ExecutionResult<TData>>` so tests can consume events via `for await` or manual `next()` calls\n * - throws if the subscription fails to produce an iterator (e.g. a validation error), surfacing the underlying errors\n *\n * @param schema The executable `GraphQLSchema`, or a factory that resolves one (useful when schema construction is async).\n * @param createContext A context creation function. Any args supplied after the request are forwarded to it,\n * so test fixtures can drive auth/user state per-call (e.g. `(jwtPayload?) => Promise<GraphQLContext>`).\n */\nexport function buildSubscribeOperation<TContext extends AnyGraphqlContext, TContextFunction extends (...args: any) => Promise<TContext>>(\n schema: GraphQLSchema | (() => Promise<GraphQLSchema>),\n createContext: TContextFunction,\n) {\n return async function subscribeOperation<TData = Record<string, unknown>, TVariables extends VariableValues = VariableValues>(\n { subscription, variables, operationName }: TypedSubscribeRequest<TData, TVariables>,\n ...createContextArgs: Parameters<TContextFunction>\n ): Promise<AsyncIterableIterator<ExecutionResult<TData>>> {\n const resolvedSchema = typeof schema === 'function' ? await schema() : schema\n const document = typeof subscription === 'string' ? parse(subscription) : subscription\n const contextValue = await createContext(...createContextArgs)\n const result = await subscribe({\n schema: resolvedSchema,\n document,\n contextValue,\n variableValues: variables as VariableValues | undefined,\n operationName,\n })\n if (Symbol.asyncIterator in result) return result as AsyncIterableIterator<ExecutionResult<TData>>\n throw new Error(`Subscription did not produce an iterator: ${JSON.stringify((result as ExecutionResult).errors)}`)\n }\n}\n"],"names":["parse","subscribe"],"mappings":";;;;AAYA;;;;;;;;;;;;;;;;;;AAkBG;AACG,SAAU,uBAAuB,CACrC,MAAsD,EACtD,aAA+B,EAAA;AAE/B,IAAA,OAAO,eAAe,kBAAkB,CACtC,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAA4C,EACpF,GAAG,iBAA+C,EAAA;AAElD,QAAA,MAAM,cAAc,GAAG,OAAO,MAAM,KAAK,UAAU,GAAG,MAAM,MAAM,EAAE,GAAG,MAAM;AAC7E,QAAA,MAAM,QAAQ,GAAG,OAAO,YAAY,KAAK,QAAQ,GAAGA,aAAK,CAAC,YAAY,CAAC,GAAG,YAAY;QACtF,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,GAAG,iBAAiB,CAAC;AAC9D,QAAA,MAAM,MAAM,GAAG,MAAMC,iBAAS,CAAC;AAC7B,YAAA,MAAM,EAAE,cAAc;YACtB,QAAQ;YACR,YAAY;AACZ,YAAA,cAAc,EAAE,SAAuC;YACvD,aAAa;AACd,SAAA,CAAC;AACF,QAAA,IAAI,MAAM,CAAC,aAAa,IAAI,MAAM;AAAE,YAAA,OAAO,MAAuD;AAClG,QAAA,MAAM,IAAI,KAAK,CAAC,CAAA,0CAAA,EAA6C,IAAI,CAAC,SAAS,CAAE,MAA0B,CAAC,MAAM,CAAC,CAAA,CAAE,CAAC;AACpH,IAAA,CAAC;AACH;;;;"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { parse, subscribe } from 'graphql';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns a `subscribeOperation` test helper for the provided schema and context creation function.
|
|
5
|
+
*
|
|
6
|
+
* The returned function runs a subscription operation directly against the schema using `graphql.subscribe`,
|
|
7
|
+
* bypassing the websocket / `graphql-ws` transport. This gives tests complete control over JWT payloads
|
|
8
|
+
* and other context inputs, the same way `buildExecuteOperation` does for queries and mutations in
|
|
9
|
+
* `@makerx/graphql-apollo-server/testing`.
|
|
10
|
+
*
|
|
11
|
+
* The returned function:
|
|
12
|
+
* - is strongly typed to the GraphQL context
|
|
13
|
+
* - accepts `TypedDocumentNode` subscriptions to provide strong operation typing
|
|
14
|
+
* - forwards any additional arguments to the supplied context creation function
|
|
15
|
+
* - returns the subscription's `AsyncIterableIterator<ExecutionResult<TData>>` so tests can consume events via `for await` or manual `next()` calls
|
|
16
|
+
* - throws if the subscription fails to produce an iterator (e.g. a validation error), surfacing the underlying errors
|
|
17
|
+
*
|
|
18
|
+
* @param schema The executable `GraphQLSchema`, or a factory that resolves one (useful when schema construction is async).
|
|
19
|
+
* @param createContext A context creation function. Any args supplied after the request are forwarded to it,
|
|
20
|
+
* so test fixtures can drive auth/user state per-call (e.g. `(jwtPayload?) => Promise<GraphQLContext>`).
|
|
21
|
+
*/
|
|
22
|
+
function buildSubscribeOperation(schema, createContext) {
|
|
23
|
+
return async function subscribeOperation({ subscription, variables, operationName }, ...createContextArgs) {
|
|
24
|
+
const resolvedSchema = typeof schema === 'function' ? await schema() : schema;
|
|
25
|
+
const document = typeof subscription === 'string' ? parse(subscription) : subscription;
|
|
26
|
+
const contextValue = await createContext(...createContextArgs);
|
|
27
|
+
const result = await subscribe({
|
|
28
|
+
schema: resolvedSchema,
|
|
29
|
+
document,
|
|
30
|
+
contextValue,
|
|
31
|
+
variableValues: variables,
|
|
32
|
+
operationName,
|
|
33
|
+
});
|
|
34
|
+
if (Symbol.asyncIterator in result)
|
|
35
|
+
return result;
|
|
36
|
+
throw new Error(`Subscription did not produce an iterator: ${JSON.stringify(result.errors)}`);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export { buildSubscribeOperation };
|
|
41
|
+
//# sourceMappingURL=subscribe-operation.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscribe-operation.mjs","sources":["../../src/testing/subscribe-operation.ts"],"sourcesContent":["import type { TypedDocumentNode } from '@graphql-typed-document-node/core'\nimport { parse, subscribe, type DocumentNode, type ExecutionResult, type GraphQLSchema } from 'graphql'\nimport type { AnyGraphqlContext } from '../context'\n\nexport type VariableValues = { [key: string]: any }\n\nexport type TypedSubscribeRequest<TData = Record<string, unknown>, TVariables extends VariableValues = VariableValues> = {\n subscription: string | DocumentNode | TypedDocumentNode<TData, TVariables>\n variables?: TVariables\n operationName?: string\n}\n\n/**\n * Returns a `subscribeOperation` test helper for the provided schema and context creation function.\n *\n * The returned function runs a subscription operation directly against the schema using `graphql.subscribe`,\n * bypassing the websocket / `graphql-ws` transport. This gives tests complete control over JWT payloads\n * and other context inputs, the same way `buildExecuteOperation` does for queries and mutations in\n * `@makerx/graphql-apollo-server/testing`.\n *\n * The returned function:\n * - is strongly typed to the GraphQL context\n * - accepts `TypedDocumentNode` subscriptions to provide strong operation typing\n * - forwards any additional arguments to the supplied context creation function\n * - returns the subscription's `AsyncIterableIterator<ExecutionResult<TData>>` so tests can consume events via `for await` or manual `next()` calls\n * - throws if the subscription fails to produce an iterator (e.g. a validation error), surfacing the underlying errors\n *\n * @param schema The executable `GraphQLSchema`, or a factory that resolves one (useful when schema construction is async).\n * @param createContext A context creation function. Any args supplied after the request are forwarded to it,\n * so test fixtures can drive auth/user state per-call (e.g. `(jwtPayload?) => Promise<GraphQLContext>`).\n */\nexport function buildSubscribeOperation<TContext extends AnyGraphqlContext, TContextFunction extends (...args: any) => Promise<TContext>>(\n schema: GraphQLSchema | (() => Promise<GraphQLSchema>),\n createContext: TContextFunction,\n) {\n return async function subscribeOperation<TData = Record<string, unknown>, TVariables extends VariableValues = VariableValues>(\n { subscription, variables, operationName }: TypedSubscribeRequest<TData, TVariables>,\n ...createContextArgs: Parameters<TContextFunction>\n ): Promise<AsyncIterableIterator<ExecutionResult<TData>>> {\n const resolvedSchema = typeof schema === 'function' ? await schema() : schema\n const document = typeof subscription === 'string' ? parse(subscription) : subscription\n const contextValue = await createContext(...createContextArgs)\n const result = await subscribe({\n schema: resolvedSchema,\n document,\n contextValue,\n variableValues: variables as VariableValues | undefined,\n operationName,\n })\n if (Symbol.asyncIterator in result) return result as AsyncIterableIterator<ExecutionResult<TData>>\n throw new Error(`Subscription did not produce an iterator: ${JSON.stringify((result as ExecutionResult).errors)}`)\n }\n}\n"],"names":[],"mappings":";;AAYA;;;;;;;;;;;;;;;;;;AAkBG;AACG,SAAU,uBAAuB,CACrC,MAAsD,EACtD,aAA+B,EAAA;AAE/B,IAAA,OAAO,eAAe,kBAAkB,CACtC,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAA4C,EACpF,GAAG,iBAA+C,EAAA;AAElD,QAAA,MAAM,cAAc,GAAG,OAAO,MAAM,KAAK,UAAU,GAAG,MAAM,MAAM,EAAE,GAAG,MAAM;AAC7E,QAAA,MAAM,QAAQ,GAAG,OAAO,YAAY,KAAK,QAAQ,GAAG,KAAK,CAAC,YAAY,CAAC,GAAG,YAAY;QACtF,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,GAAG,iBAAiB,CAAC;AAC9D,QAAA,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC;AAC7B,YAAA,MAAM,EAAE,cAAc;YACtB,QAAQ;YACR,YAAY;AACZ,YAAA,cAAc,EAAE,SAAuC;YACvD,aAAa;AACd,SAAA,CAAC;AACF,QAAA,IAAI,MAAM,CAAC,aAAa,IAAI,MAAM;AAAE,YAAA,OAAO,MAAuD;AAClG,QAAA,MAAM,IAAI,KAAK,CAAC,CAAA,0CAAA,EAA6C,IAAI,CAAC,SAAS,CAAE,MAA0B,CAAC,MAAM,CAAC,CAAA,CAAE,CAAC;AACpH,IAAA,CAAC;AACH;;;;"}
|