graphql
Version:
A Query Language and Runtime which can target any service.
195 lines • 7.68 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IncrementalPublisher = void 0;
const Path_ts_1 = require("../../jsutils/Path.js");
const ensureGraphQLError_ts_1 = require("../../error/ensureGraphQLError.js");
const mapAsyncIterable_ts_1 = require("../mapAsyncIterable.js");
const withConcurrentAbruptClose_ts_1 = require("../withConcurrentAbruptClose.js");
const WorkQueue_ts_1 = require("./WorkQueue.js");
class IncrementalPublisher {
constructor() {
this._ids = new Map();
this._nextId = 0;
}
buildResponse(data, errors, work, abortSignal, onFinished) {
const { initialGroups, initialStreams, events } = (0, WorkQueue_ts_1.createWorkQueue)(work);
function abort() {
subsequentResults.throw(abortSignal?.reason).catch(() => {
});
}
if (abortSignal) {
abortSignal.addEventListener('abort', abort);
}
const onWorkQueueFinished = () => {
onFinished();
abortSignal?.removeEventListener('abort', abort);
};
const pending = this._toPendingResults(initialGroups, initialStreams);
const initialResult = errors.length
? { errors, data, pending, hasNext: true }
: { data, pending, hasNext: true };
const subsequentResults = (0, withConcurrentAbruptClose_ts_1.withConcurrentAbruptClose)((0, mapAsyncIterable_ts_1.mapAsyncIterable)(events, (batch) => this._handleBatch(batch, onWorkQueueFinished)), () => onWorkQueueFinished());
return {
initialResult,
subsequentResults,
};
}
_ensureId(deferredFragmentOrStream) {
let id = this._ids.get(deferredFragmentOrStream);
if (id !== undefined) {
return id;
}
id = String(this._nextId++);
this._ids.set(deferredFragmentOrStream, id);
return id;
}
_toPendingResults(newGroups, newStreams) {
const pendingResults = [];
for (const collection of [newGroups, newStreams]) {
for (const node of collection) {
const id = this._ensureId(node);
const pendingResult = {
id,
path: (0, Path_ts_1.pathToArray)(node.path),
};
if (node.label !== undefined) {
pendingResult.label = node.label;
}
pendingResults.push(pendingResult);
}
}
return pendingResults;
}
_handleBatch(batch, onWorkQueueFinished) {
const context = {
pending: [],
incremental: [],
completed: [],
hasNext: true,
};
for (const event of batch) {
this._handleWorkQueueEvent(event, context, onWorkQueueFinished);
}
const { incremental, completed, pending, hasNext } = context;
const result = { hasNext };
if (pending.length > 0) {
result.pending = pending;
}
if (incremental.length > 0) {
result.incremental = incremental;
}
if (completed.length > 0) {
result.completed = completed;
}
return result;
}
_handleWorkQueueEvent(event, context, onWorkQueueFinished) {
switch (event.kind) {
case 'GROUP_VALUES': {
const group = event.group;
const id = this._ensureId(group);
for (const value of event.values) {
const { bestId, subPath } = this._getBestIdAndSubPath(id, group, value);
const incrementalEntry = {
id: bestId,
data: value.data,
};
if (value.errors !== undefined) {
incrementalEntry.errors = value.errors;
}
if (subPath !== undefined) {
incrementalEntry.subPath = subPath;
}
context.incremental.push(incrementalEntry);
}
break;
}
case 'GROUP_SUCCESS': {
const group = event.group;
const id = this._ensureId(group);
context.completed.push({ id });
this._ids.delete(group);
if (event.newGroups.length > 0 || event.newStreams.length > 0) {
context.pending.push(...this._toPendingResults(event.newGroups, event.newStreams));
}
break;
}
case 'GROUP_FAILURE': {
const { group, error } = event;
const id = this._ensureId(group);
context.completed.push({
id,
errors: [(0, ensureGraphQLError_ts_1.ensureGraphQLError)(error)],
});
this._ids.delete(group);
break;
}
case 'STREAM_VALUES': {
const stream = event.stream;
const id = this._ensureId(stream);
const { values, newGroups, newStreams } = event;
const items = [];
const errors = [];
for (const value of values) {
items.push(value.item);
if (value.errors !== undefined) {
errors.push(...value.errors);
}
}
context.incremental.push(errors.length > 0 ? { id, items, errors } : { id, items });
if (newGroups.length > 0 || newStreams.length > 0) {
context.pending.push(...this._toPendingResults(newGroups, newStreams));
}
break;
}
case 'STREAM_SUCCESS': {
const stream = event.stream;
context.completed.push({
id: this._ensureId(stream),
});
this._ids.delete(stream);
break;
}
case 'STREAM_FAILURE': {
const stream = event.stream;
context.completed.push({
id: this._ensureId(stream),
errors: [(0, ensureGraphQLError_ts_1.ensureGraphQLError)(event.error)],
});
this._ids.delete(stream);
break;
}
case 'WORK_QUEUE_TERMINATION': {
onWorkQueueFinished?.();
context.hasNext = false;
break;
}
}
}
_getBestIdAndSubPath(initialId, initialDeferredFragmentRecord, executionGroupValue) {
let maxLength = (0, Path_ts_1.pathToArray)(initialDeferredFragmentRecord.path).length;
let bestId = initialId;
for (const deliveryGroup of executionGroupValue.deliveryGroups) {
if (deliveryGroup === initialDeferredFragmentRecord) {
continue;
}
const id = this._ids.get(deliveryGroup);
if (id === undefined) {
continue;
}
const path = (0, Path_ts_1.pathToArray)(deliveryGroup.path);
const length = path.length;
if (length > maxLength) {
maxLength = length;
bestId = id;
}
}
const subPath = executionGroupValue.path.slice(maxLength);
return {
bestId,
subPath: subPath.length > 0 ? subPath : undefined,
};
}
}
exports.IncrementalPublisher = IncrementalPublisher;
//# sourceMappingURL=IncrementalPublisher.js.map