@graphql-hive/gateway-plugin-console-sdk
Version:
Hive Console + Hive Gateway
239 lines (238 loc) • 12.3 kB
JavaScript
import { __asyncGenerator, __asyncValues, __await } from "tslib";
import { OperationTypeNode, parse, responsePathAsArray, } from 'graphql';
import { lru } from 'tiny-lru';
import { addHiveTypenames, createHive as createHiveClient, getDefinedRootType, hideInjectedTypenames, isAsyncIterable, isHiveClient, } from '@graphql-hive/core';
import { isEntityRequest } from './is-entity-request.js';
import { version } from './version.js';
export { atLeastOnceSampler, createSchemaFetcher, createServicesFetcher, createSupergraphSDLFetcher, } from '@graphql-hive/core';
export function createHive(clientOrOptions) {
return createHiveClient(Object.assign(Object.assign({}, clientOrOptions), { agent: Object.assign({ name: 'hive-client-gateway-sdk', version }, clientOrOptions.agent) }));
}
export function useHive(clientOrOptions) {
var _a, _b;
const hive = isHiveClient(clientOrOptions)
? clientOrOptions
: createHive(Object.assign(Object.assign({}, clientOrOptions), { agent: Object.assign({ name: 'hive-client-gateway-sdk' }, clientOrOptions.agent) }));
void hive.info();
const contextualCache = new WeakMap();
const statusMap = new WeakMap();
const fieldLevelMetricsEnabled = isHiveClient(clientOrOptions)
? false
: (typeof clientOrOptions.usage === 'object' &&
((_a = clientOrOptions.usage) === null || _a === void 0 ? void 0 : _a.fieldLevelMetricsEnabled)) ||
false;
const operationCache = fieldLevelMetricsEnabled
? lru(isHiveClient(clientOrOptions) ? 10000 : ((_b = clientOrOptions.cache) !== null && _b !== void 0 ? _b : 10000))
: null;
let latestSchema = null;
return {
onFetch({ executionRequest }) {
if (!executionRequest) {
return;
}
return function onFetchDone({ response }) {
statusMap.set(executionRequest, response.status);
};
},
onSubgraphExecute({ executionRequest, subgraphName, subgraph: subgraphSchema }) {
var _a, _b, _c;
if (!fieldLevelMetricsEnabled || !executionRequest.context) {
return;
}
const cache = contextualCache.get(executionRequest.context);
if (!cache) {
return;
}
const finishSubRequest = cache.collector.subrequest({
subgraph: subgraphName,
type: isEntityRequest(executionRequest.document) ? 'ENTITY' : 'ROOT',
paths: ((_a = executionRequest.info) === null || _a === void 0 ? void 0 : _a.path)
? [responsePathAsArray(executionRequest.info.path).join('.')]
: (_c = getDefinedRootType(subgraphSchema, (_b = executionRequest.operationType) !== null && _b !== void 0 ? _b : OperationTypeNode.QUERY)) === null || _c === void 0 ? void 0 : _c.name,
});
return function onSubgraphExecuteDone({ result }) {
var _a;
if (!isAsyncIterable(result)) {
finishSubRequest({
status: (_a = statusMap.get(executionRequest)) !== null && _a !== void 0 ? _a : 200,
subgraphSchema,
result,
// The Rust query planner generates and caches this document up front.
// This depends on the operation and variables.
// So if we need to add typenames to the request, it needs to happen
// here also. E.g. `addHiveTypenames(executionRequest.document, subgraphSchema)`.
// But for the js gateway execution, this is redundant and an unnecessary expense.
document: executionRequest.document,
});
}
};
},
onSchemaChange({ schema }) {
hive.reportSchema({ schema });
latestSchema = schema;
operationCache === null || operationCache === void 0 ? void 0 : operationCache.clear();
},
onParams(context) {
if ((context.params.query || 'documentId' in context.params) && latestSchema) {
contextualCache.set(context.context, {
collector: hive.collectUsage(),
paramsArgs: context.params,
});
}
},
onParse(parseCtx) {
return ctx => {
if (ctx.result.kind === 'Document') {
const cache = contextualCache.get(ctx.context);
if (cache) {
cache.document = ctx.result;
}
if (fieldLevelMetricsEnabled && operationCache) {
// We need __typename on every object in the subgraph result so we can
// resolve abstract types (unions/interfaces) to concrete type coordinates
// when recording field-level metrics downstream.
// This is done here for more performant caching of the result.
const query = parseCtx.params.source;
const cachedDocument = operationCache.get(query);
if (cachedDocument) {
if (cachedDocument !== true) {
parseCtx.setParsedDocument(cachedDocument);
if (cache) {
cache.document = cachedDocument;
}
}
}
else if (latestSchema) {
const modifiedDocument = addHiveTypenames(ctx.result, latestSchema);
operationCache.set(query, ctx.result === modifiedDocument || modifiedDocument);
if (ctx.result !== modifiedDocument) {
parseCtx.setParsedDocument(modifiedDocument);
if (cache) {
cache.document = modifiedDocument;
}
}
}
}
}
};
},
onValidate({ context }) {
return function afterValidate({ result }) {
const cache = contextualCache.get(context);
if (cache && result.length > 0) {
// Type casting since older Hive clients may lack strict typings for trackGatewayErrors
cache.collector.trackGatewayErrors({ errors: result });
}
};
},
onExecute({ args, context }) {
const ctx = args.contextValue || context;
const cache = contextualCache.get(ctx);
if (!cache)
return;
cache.executionArgs = args;
args.document = cache.document;
return {
onExecuteDone({ result }) {
var _a;
if (!isAsyncIterable(result)) {
ctx.waitUntil(cache.collector.finish(Object.assign(Object.assign({}, args), { document: (_a = cache.document) !== null && _a !== void 0 ? _a : args.document }), result, cache.experimental__documentId));
return;
}
const errors = [];
return {
onNext({ result: iterResult }) {
if (iterResult.errors) {
errors.push(...iterResult.errors);
}
},
onEnd() {
var _a;
ctx.waitUntil(cache.collector.finish(Object.assign(Object.assign({}, args), { document: (_a = cache.document) !== null && _a !== void 0 ? _a : args.document }),
// how to pass in "data" here?
errors.length ? { errors } : {}, cache.experimental__documentId));
},
};
},
};
},
onSubscribe({ args, context }) {
const ctx = args.contextValue || context;
const record = contextualCache.get(ctx);
return {
onSubscribeResult() {
var _a, _b, _c;
const experimental__persistedDocumentHash = record === null || record === void 0 ? void 0 : record.experimental__documentId;
hive.collectSubscriptionUsage({
args: Object.assign(Object.assign({}, args), { document: (_c = (_a = record === null || record === void 0 ? void 0 : record.document) !== null && _a !== void 0 ? _a : (_b = record === null || record === void 0 ? void 0 : record.executionArgs) === null || _b === void 0 ? void 0 : _b.document) !== null && _c !== void 0 ? _c : args.document }),
experimental__persistedDocumentHash,
});
},
};
},
async onResultProcess({ serverContext, result, setResult }) {
const record = contextualCache.get(serverContext);
if (!record || Array.isArray(result) || isAsyncIterable(result) || record.executionArgs) {
if (fieldLevelMetricsEnabled) {
if (isAsyncIterable(result)) {
const modifyStream = function () {
return __asyncGenerator(this, arguments, function* () {
var _a, e_1, _b, _c;
try {
for (var _d = true, result_1 = __asyncValues(result), result_1_1; result_1_1 = yield __await(result_1.next()), _a = result_1_1.done, !_a; _d = true) {
_c = result_1_1.value;
_d = false;
const r = _c;
hideInjectedTypenames(r.data);
yield yield __await(r);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_d && !_a && (_b = result_1.return)) yield __await(_b.call(result_1));
}
finally { if (e_1) throw e_1.error; }
}
});
};
setResult(modifyStream());
}
else if (Array.isArray(result)) {
for (let r of result) {
hideInjectedTypenames(r.data);
}
}
else {
hideInjectedTypenames(result.data);
}
}
return;
}
if (record.paramsArgs.query && latestSchema) {
try {
let doc = record.document;
if (!doc) {
doc = parse(record.paramsArgs.query);
}
serverContext.waitUntil(record.collector.finish({
document: doc,
schema: latestSchema,
variableValues: record.paramsArgs.variables,
operationName: record.paramsArgs.operationName,
contextValue: serverContext,
}, result, record.experimental__documentId));
}
catch (err) {
// Fail silently if parse fails inside cached block handling
}
}
if (fieldLevelMetricsEnabled) {
hideInjectedTypenames(result.data);
}
},
onDispose() {
return hive.dispose();
},
};
}