Skip to content

Commit d2e280a

Browse files
authored
incremental: utilize id and subPath rather than path and label (#3960)
updates response format to fully match new format further discussed at graphql/defer-stream-wg#69
1 parent d1d66a3 commit d2e280a

File tree

4 files changed

+406
-353
lines changed

4 files changed

+406
-353
lines changed

src/execution/IncrementalPublisher.ts

Lines changed: 63 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ export interface IncrementalDeferResult<
100100
> {
101101
errors?: ReadonlyArray<GraphQLError>;
102102
data: TData;
103-
path: ReadonlyArray<string | number>;
103+
id: string;
104+
subPath?: ReadonlyArray<string | number>;
104105
extensions?: TExtensions;
105106
}
106107

@@ -110,7 +111,8 @@ export interface FormattedIncrementalDeferResult<
110111
> {
111112
errors?: ReadonlyArray<GraphQLFormattedError>;
112113
data: TData;
113-
path: ReadonlyArray<string | number>;
114+
id: string;
115+
subPath?: ReadonlyArray<string | number>;
114116
extensions?: TExtensions;
115117
}
116118

@@ -120,7 +122,8 @@ export interface IncrementalStreamResult<
120122
> {
121123
errors?: ReadonlyArray<GraphQLError>;
122124
items: TData;
123-
path: ReadonlyArray<string | number>;
125+
id: string;
126+
subPath?: ReadonlyArray<string | number>;
124127
extensions?: TExtensions;
125128
}
126129

@@ -130,7 +133,8 @@ export interface FormattedIncrementalStreamResult<
130133
> {
131134
errors?: ReadonlyArray<GraphQLFormattedError>;
132135
items: TData;
133-
path: ReadonlyArray<string | number>;
136+
id: string;
137+
subPath?: ReadonlyArray<string | number>;
134138
extensions?: TExtensions;
135139
}
136140

@@ -146,13 +150,13 @@ export type FormattedIncrementalResult<
146150
| FormattedIncrementalStreamResult<TData, TExtensions>;
147151

148152
export interface PendingResult {
153+
id: string;
149154
path: ReadonlyArray<string | number>;
150155
label?: string;
151156
}
152157

153158
export interface CompletedResult {
154-
path: ReadonlyArray<string | number>;
155-
label?: string;
159+
id: string;
156160
errors?: ReadonlyArray<GraphQLError>;
157161
}
158162

@@ -178,6 +182,7 @@ export interface FormattedCompletedResult {
178182
* @internal
179183
*/
180184
export class IncrementalPublisher {
185+
private _nextId = 0;
181186
private _released: Set<SubsequentResultRecord>;
182187
private _pending: Set<SubsequentResultRecord>;
183188

@@ -372,7 +377,10 @@ export class IncrementalPublisher {
372377
const pendingResults: Array<PendingResult> = [];
373378
for (const pendingSource of pendingSources) {
374379
pendingSource.pendingSent = true;
380+
const id = this._getNextId();
381+
pendingSource.id = id;
375382
const pendingResult: PendingResult = {
383+
id,
376384
path: pendingSource.path,
377385
};
378386
if (pendingSource.label !== undefined) {
@@ -383,6 +391,10 @@ export class IncrementalPublisher {
383391
return pendingResults;
384392
}
385393

394+
private _getNextId(): string {
395+
return String(this._nextId++);
396+
}
397+
386398
private _subscribe(): AsyncGenerator<
387399
SubsequentIncrementalExecutionResult,
388400
void,
@@ -554,7 +566,9 @@ export class IncrementalPublisher {
554566
}
555567
const incrementalResult: IncrementalStreamResult = {
556568
items: subsequentResultRecord.items,
557-
path: subsequentResultRecord.streamRecord.path,
569+
// safe because `id` is defined once the stream has been released as pending
570+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
571+
id: subsequentResultRecord.streamRecord.id!,
558572
};
559573
if (subsequentResultRecord.errors.length > 0) {
560574
incrementalResult.errors = subsequentResultRecord.errors;
@@ -571,11 +585,8 @@ export class IncrementalPublisher {
571585
for (const deferredGroupedFieldSetRecord of subsequentResultRecord.deferredGroupedFieldSetRecords) {
572586
if (!deferredGroupedFieldSetRecord.sent) {
573587
deferredGroupedFieldSetRecord.sent = true;
574-
const incrementalResult: IncrementalDeferResult = {
575-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
576-
data: deferredGroupedFieldSetRecord.data!,
577-
path: deferredGroupedFieldSetRecord.path,
578-
};
588+
const incrementalResult: IncrementalDeferResult =
589+
this._getIncrementalDeferResult(deferredGroupedFieldSetRecord);
579590
if (deferredGroupedFieldSetRecord.errors.length > 0) {
580591
incrementalResult.errors = deferredGroupedFieldSetRecord.errors;
581592
}
@@ -592,15 +603,49 @@ export class IncrementalPublisher {
592603
};
593604
}
594605

606+
private _getIncrementalDeferResult(
607+
deferredGroupedFieldSetRecord: DeferredGroupedFieldSetRecord,
608+
): IncrementalDeferResult {
609+
const { data, deferredFragmentRecords } = deferredGroupedFieldSetRecord;
610+
let maxLength = deferredFragmentRecords[0].path.length;
611+
let maxIndex = 0;
612+
for (let i = 1; i < deferredFragmentRecords.length; i++) {
613+
const deferredFragmentRecord = deferredFragmentRecords[i];
614+
const length = deferredFragmentRecord.path.length;
615+
if (length > maxLength) {
616+
maxLength = length;
617+
maxIndex = i;
618+
}
619+
}
620+
const recordWithLongestPath = deferredFragmentRecords[maxIndex];
621+
const longestPath = recordWithLongestPath.path;
622+
const subPath = deferredGroupedFieldSetRecord.path.slice(
623+
longestPath.length,
624+
);
625+
const id = recordWithLongestPath.id;
626+
const incrementalDeferResult: IncrementalDeferResult = {
627+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
628+
data: data!,
629+
// safe because `id` is defined once the fragment has been released as pending
630+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
631+
id: id!,
632+
};
633+
634+
if (subPath.length > 0) {
635+
incrementalDeferResult.subPath = subPath;
636+
}
637+
638+
return incrementalDeferResult;
639+
}
640+
595641
private _completedRecordToResult(
596642
completedRecord: DeferredFragmentRecord | StreamRecord,
597643
): CompletedResult {
598644
const result: CompletedResult = {
599-
path: completedRecord.path,
645+
// safe because `id` is defined once the stream has been released as pending
646+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
647+
id: completedRecord.id!,
600648
};
601-
if (completedRecord.label !== undefined) {
602-
result.label = completedRecord.label;
603-
}
604649
if (completedRecord.errors.length > 0) {
605650
result.errors = completedRecord.errors;
606651
}
@@ -736,6 +781,7 @@ export class DeferredGroupedFieldSetRecord {
736781
export class DeferredFragmentRecord {
737782
path: ReadonlyArray<string | number>;
738783
label: string | undefined;
784+
id: string | undefined;
739785
children: Set<SubsequentResultRecord>;
740786
deferredGroupedFieldSetRecords: Set<DeferredGroupedFieldSetRecord>;
741787
errors: Array<GraphQLError>;
@@ -758,6 +804,7 @@ export class DeferredFragmentRecord {
758804
export class StreamRecord {
759805
label: string | undefined;
760806
path: ReadonlyArray<string | number>;
807+
id: string | undefined;
761808
errors: Array<GraphQLError>;
762809
earlyReturn?: (() => Promise<unknown>) | undefined;
763810
pendingSent?: boolean;

0 commit comments

Comments
 (0)