@graphql-hive/gateway-plugin-console-sdk
Version:
Hive Console + Hive Gateway
222 lines (221 loc) • 12.2 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,
});
}
},
onExecute({ args, context, setExecuteFn, executeFn }) {
var _a;
const ctx = args.contextValue || context;
const cache = contextualCache.get(ctx);
if (!cache)
return;
cache.executionArgs = args;
if (fieldLevelMetricsEnabled && operationCache && latestSchema) {
// Validation must run against the client document. Add the metadata fields only
// to the document passed to execution and cache that transformed document.
const query = (_a = args.document.loc) === null || _a === void 0 ? void 0 : _a.source.body;
const cachedDocument = query ? operationCache.get(query) : undefined;
const modifiedDocument = cachedDocument === true
? args.document
: cachedDocument || addHiveTypenames(args.document, latestSchema);
if (query && cachedDocument === undefined) {
operationCache.set(query, args.document === modifiedDocument || modifiedDocument);
}
if (args.document !== modifiedDocument) {
setExecuteFn(executionArgs => executeFn(Object.assign(Object.assign({}, executionArgs), { document: modifiedDocument })));
}
cache.document = modifiedDocument;
}
return {
onExecuteDone({ result, args }) {
var _a, _b, _c;
if (!isAsyncIterable(result)) {
if (result.errors) {
const gatewayErrors = (_a = result.errors) === null || _a === void 0 ? void 0 : _a.filter(e => { var _a, _b;
/**
* DOWNSTREAM_SERVICE_ERROR is set by the executor to wrap subgraph errors. A serviceName is also
* used to indicate the source of the error. If either of these are found, then it's safe to assume
* the error was not thrown in the gateway.
*/
return ((_a = e.extensions) === null || _a === void 0 ? void 0 : _a.code) !== 'DOWNSTREAM_SERVICE_ERROR' && !((_b = e.extensions) === null || _b === void 0 ? void 0 : _b.serviceName); });
if (cache && (gatewayErrors === null || gatewayErrors === void 0 ? void 0 : gatewayErrors.length) > 0) {
cache.collector.trackGatewayErrors({ errors: gatewayErrors });
}
}
args.contextValue.waitUntil(cache.collector.finish(Object.assign(Object.assign({}, args), { document: (_c = (_b = cache.document) !== null && _b !== void 0 ? _b : ctx.document) !== null && _c !== void 0 ? _c : 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();
},
};
}