Skip to content

Commit

Permalink
refactor: extract execution types to separate file
Browse files Browse the repository at this point in the history
  • Loading branch information
yaacovCR committed May 30, 2024
1 parent e15c3ec commit 53c1902
Show file tree
Hide file tree
Showing 11 changed files with 339 additions and 311 deletions.
324 changes: 25 additions & 299 deletions src/execution/IncrementalPublisher.ts
Original file line number Diff line number Diff line change
@@ -1,174 +1,35 @@
import { invariant } from '../jsutils/invariant.js';
import { isPromise } from '../jsutils/isPromise.js';
import type { ObjMap } from '../jsutils/ObjMap.js';
import type { Path } from '../jsutils/Path.js';
import { pathToArray } from '../jsutils/Path.js';
import type { PromiseOrValue } from '../jsutils/PromiseOrValue.js';
import { promiseWithResolvers } from '../jsutils/promiseWithResolvers.js';

import type {
GraphQLError,
GraphQLFormattedError,
} from '../error/GraphQLError.js';

/**
* The result of GraphQL execution.
*
* - `errors` is included when any errors occurred as a non-empty array.
* - `data` is the result of a successful execution of the query.
* - `hasNext` is true if a future payload is expected.
* - `extensions` is reserved for adding non-standard properties.
* - `incremental` is a list of the results from defer/stream directives.
*/
export interface ExecutionResult<
TData = ObjMap<unknown>,
TExtensions = ObjMap<unknown>,
> {
errors?: ReadonlyArray<GraphQLError>;
data?: TData | null;
extensions?: TExtensions;
}

export interface FormattedExecutionResult<
TData = ObjMap<unknown>,
TExtensions = ObjMap<unknown>,
> {
errors?: ReadonlyArray<GraphQLFormattedError>;
data?: TData | null;
extensions?: TExtensions;
}

export interface ExperimentalIncrementalExecutionResults<
TData = unknown,
TExtensions = ObjMap<unknown>,
> {
initialResult: InitialIncrementalExecutionResult<TData, TExtensions>;
subsequentResults: AsyncGenerator<
SubsequentIncrementalExecutionResult<TData, TExtensions>,
void,
void
>;
}

export interface InitialIncrementalExecutionResult<
TData = ObjMap<unknown>,
TExtensions = ObjMap<unknown>,
> extends ExecutionResult<TData, TExtensions> {
data: TData;
pending: ReadonlyArray<PendingResult>;
hasNext: true;
extensions?: TExtensions;
}

export interface FormattedInitialIncrementalExecutionResult<
TData = ObjMap<unknown>,
TExtensions = ObjMap<unknown>,
> extends FormattedExecutionResult<TData, TExtensions> {
data: TData;
pending: ReadonlyArray<PendingResult>;
hasNext: boolean;
extensions?: TExtensions;
}

export interface SubsequentIncrementalExecutionResult<
TData = unknown,
TExtensions = ObjMap<unknown>,
> {
pending?: ReadonlyArray<PendingResult>;
incremental?: ReadonlyArray<IncrementalResult<TData, TExtensions>>;
completed?: ReadonlyArray<CompletedResult>;
hasNext: boolean;
extensions?: TExtensions;
}

export interface FormattedSubsequentIncrementalExecutionResult<
TData = unknown,
TExtensions = ObjMap<unknown>,
> {
hasNext: boolean;
pending?: ReadonlyArray<PendingResult>;
incremental?: ReadonlyArray<FormattedIncrementalResult<TData, TExtensions>>;
completed?: ReadonlyArray<FormattedCompletedResult>;
extensions?: TExtensions;
}

interface BareDeferredGroupedFieldSetResult<TData = ObjMap<unknown>> {
errors?: ReadonlyArray<GraphQLError>;
data: TData;
}

export interface IncrementalDeferResult<
TData = ObjMap<unknown>,
TExtensions = ObjMap<unknown>,
> extends BareDeferredGroupedFieldSetResult<TData> {
id: string;
subPath?: ReadonlyArray<string | number>;
extensions?: TExtensions;
}
import type { GraphQLError } from '../error/GraphQLError.js';

export interface FormattedIncrementalDeferResult<
TData = ObjMap<unknown>,
TExtensions = ObjMap<unknown>,
> {
errors?: ReadonlyArray<GraphQLFormattedError>;
data: TData;
id: string;
subPath?: ReadonlyArray<string | number>;
extensions?: TExtensions;
}

interface BareStreamItemsResult<TData = ReadonlyArray<unknown>> {
errors?: ReadonlyArray<GraphQLError>;
items: TData;
}

export interface IncrementalStreamResult<
TData = ReadonlyArray<unknown>,
TExtensions = ObjMap<unknown>,
> extends BareStreamItemsResult<TData> {
id: string;
subPath?: ReadonlyArray<string | number>;
extensions?: TExtensions;
}

export interface FormattedIncrementalStreamResult<
TData = Array<unknown>,
TExtensions = ObjMap<unknown>,
> {
errors?: ReadonlyArray<GraphQLFormattedError>;
items: TData;
id: string;
subPath?: ReadonlyArray<string | number>;
extensions?: TExtensions;
}

export type IncrementalResult<TData = unknown, TExtensions = ObjMap<unknown>> =
| IncrementalDeferResult<TData, TExtensions>
| IncrementalStreamResult<TData, TExtensions>;

export type FormattedIncrementalResult<
TData = unknown,
TExtensions = ObjMap<unknown>,
> =
| FormattedIncrementalDeferResult<TData, TExtensions>
| FormattedIncrementalStreamResult<TData, TExtensions>;

export interface PendingResult {
id: string;
path: ReadonlyArray<string | number>;
label?: string;
}

export interface CompletedResult {
id: string;
errors?: ReadonlyArray<GraphQLError>;
}

export interface FormattedCompletedResult {
path: ReadonlyArray<string | number>;
label?: string;
errors?: ReadonlyArray<GraphQLError>;
}
import type {
CancellableStreamRecord,
CompletedResult,
DeferredFragmentRecord,
DeferredGroupedFieldSetResult,
ExperimentalIncrementalExecutionResults,
IncrementalDataRecord,
IncrementalDataRecordResult,
IncrementalDeferResult,
IncrementalResult,
IncrementalStreamResult,
InitialIncrementalExecutionResult,
PendingResult,
StreamItemsResult,
SubsequentIncrementalExecutionResult,
SubsequentResultRecord,
} from './types.js';
import {
isCancellableStreamRecord,
isDeferredFragmentRecord,
isDeferredGroupedFieldSetRecord,
isDeferredGroupedFieldSetResult,
isNonReconcilableDeferredGroupedFieldSetResult,
} from './types.js';

export function buildIncrementalResponse(
context: IncrementalPublisherContext,
Expand Down Expand Up @@ -657,138 +518,3 @@ class IncrementalPublisher {
};
}
}

function isDeferredFragmentRecord(
subsequentResultRecord: SubsequentResultRecord,
): subsequentResultRecord is DeferredFragmentRecord {
return 'parent' in subsequentResultRecord;
}

function isDeferredGroupedFieldSetRecord(
incrementalDataRecord: IncrementalDataRecord,
): incrementalDataRecord is DeferredGroupedFieldSetRecord {
return 'deferredFragmentRecords' in incrementalDataRecord;
}

export type DeferredGroupedFieldSetResult =
| ReconcilableDeferredGroupedFieldSetResult
| NonReconcilableDeferredGroupedFieldSetResult;

function isDeferredGroupedFieldSetResult(
subsequentResult: DeferredGroupedFieldSetResult | StreamItemsResult,
): subsequentResult is DeferredGroupedFieldSetResult {
return 'deferredFragmentRecords' in subsequentResult;
}

interface ReconcilableDeferredGroupedFieldSetResult {
deferredFragmentRecords: ReadonlyArray<DeferredFragmentRecord>;
path: Array<string | number>;
result: BareDeferredGroupedFieldSetResult;
incrementalDataRecords: ReadonlyArray<IncrementalDataRecord> | undefined;
sent?: true | undefined;
errors?: never;
}

interface NonReconcilableDeferredGroupedFieldSetResult {
errors: ReadonlyArray<GraphQLError>;
deferredFragmentRecords: ReadonlyArray<DeferredFragmentRecord>;
path: Array<string | number>;
result?: never;
}

function isNonReconcilableDeferredGroupedFieldSetResult(
deferredGroupedFieldSetResult: DeferredGroupedFieldSetResult,
): deferredGroupedFieldSetResult is NonReconcilableDeferredGroupedFieldSetResult {
return deferredGroupedFieldSetResult.errors !== undefined;
}

export interface DeferredGroupedFieldSetRecord {
deferredFragmentRecords: ReadonlyArray<DeferredFragmentRecord>;
result: PromiseOrValue<DeferredGroupedFieldSetResult>;
}

export interface SubsequentResultRecord {
path: Path | undefined;
label: string | undefined;
id?: string | undefined;
}

/** @internal */
export class DeferredFragmentRecord implements SubsequentResultRecord {
path: Path | undefined;
label: string | undefined;
id?: string | undefined;
parent: DeferredFragmentRecord | undefined;
expectedReconcilableResults: number;
results: Array<DeferredGroupedFieldSetResult>;
reconcilableResults: Array<ReconcilableDeferredGroupedFieldSetResult>;
children: Set<DeferredFragmentRecord>;

constructor(opts: {
path: Path | undefined;
label: string | undefined;
parent: DeferredFragmentRecord | undefined;
}) {
this.path = opts.path;
this.label = opts.label;
this.parent = opts.parent;
this.expectedReconcilableResults = 0;
this.results = [];
this.reconcilableResults = [];
this.children = new Set();
}
}

export interface CancellableStreamRecord extends SubsequentResultRecord {
earlyReturn: () => Promise<unknown>;
}

function isCancellableStreamRecord(
subsequentResultRecord: SubsequentResultRecord,
): subsequentResultRecord is CancellableStreamRecord {
return 'earlyReturn' in subsequentResultRecord;
}

interface ReconcilableStreamItemsResult {
streamRecord: SubsequentResultRecord;
result: BareStreamItemsResult;
incrementalDataRecords: ReadonlyArray<IncrementalDataRecord> | undefined;
errors?: never;
}

export function isReconcilableStreamItemsResult(
streamItemsResult: StreamItemsResult,
): streamItemsResult is ReconcilableStreamItemsResult {
return streamItemsResult.result !== undefined;
}

interface TerminatingStreamItemsResult {
streamRecord: SubsequentResultRecord;
result?: never;
incrementalDataRecords?: never;
errors?: never;
}

interface NonReconcilableStreamItemsResult {
streamRecord: SubsequentResultRecord;
errors: ReadonlyArray<GraphQLError>;
result?: never;
}

export type StreamItemsResult =
| ReconcilableStreamItemsResult
| TerminatingStreamItemsResult
| NonReconcilableStreamItemsResult;

export interface StreamItemsRecord {
streamRecord: SubsequentResultRecord;
result: PromiseOrValue<StreamItemsResult>;
}

export type IncrementalDataRecord =
| DeferredGroupedFieldSetRecord
| StreamItemsRecord;

export type IncrementalDataRecordResult =
| DeferredGroupedFieldSetResult
| StreamItemsResult;
2 changes: 1 addition & 1 deletion src/execution/__tests__/defer-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { execute, experimentalExecuteIncrementally } from '../execute.js';
import type {
InitialIncrementalExecutionResult,
SubsequentIncrementalExecutionResult,
} from '../IncrementalPublisher.js';
} from '../types.js';

const friendType = new GraphQLObjectType({
fields: {
Expand Down
2 changes: 1 addition & 1 deletion src/execution/__tests__/lists-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { GraphQLSchema } from '../../type/schema.js';
import { buildSchema } from '../../utilities/buildASTSchema.js';

import { execute, executeSync } from '../execute.js';
import type { ExecutionResult } from '../IncrementalPublisher.js';
import type { ExecutionResult } from '../types.js';

describe('Execute: Accepts any iterable as list value', () => {
function complete(rootValue: unknown) {
Expand Down
2 changes: 1 addition & 1 deletion src/execution/__tests__/nonnull-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { GraphQLSchema } from '../../type/schema.js';
import { buildSchema } from '../../utilities/buildASTSchema.js';

import { execute, executeSync } from '../execute.js';
import type { ExecutionResult } from '../IncrementalPublisher.js';
import type { ExecutionResult } from '../types.js';

const syncError = new Error('sync');
const syncNonNullError = new Error('syncNonNull');
Expand Down
2 changes: 1 addition & 1 deletion src/execution/__tests__/oneof-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { parse } from '../../language/parser.js';
import { buildSchema } from '../../utilities/buildASTSchema.js';

import { execute } from '../execute.js';
import type { ExecutionResult } from '../IncrementalPublisher.js';
import type { ExecutionResult } from '../types.js';

const schema = buildSchema(`
type Query {
Expand Down
2 changes: 1 addition & 1 deletion src/execution/__tests__/stream-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { experimentalExecuteIncrementally } from '../execute.js';
import type {
InitialIncrementalExecutionResult,
SubsequentIncrementalExecutionResult,
} from '../IncrementalPublisher.js';
} from '../types.js';

const friendType = new GraphQLObjectType({
fields: {
Expand Down
2 changes: 1 addition & 1 deletion src/execution/__tests__/subscribe-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { GraphQLSchema } from '../../type/schema.js';

import type { ExecutionArgs } from '../execute.js';
import { createSourceEventStream, subscribe } from '../execute.js';
import type { ExecutionResult } from '../IncrementalPublisher.js';
import type { ExecutionResult } from '../types.js';

import { SimplePubSub } from './simplePubSub.js';

Expand Down
Loading

0 comments on commit 53c1902

Please sign in to comment.