UNPKG

@graphql-hive/gateway-plugin-console-sdk

Version:
145 lines (144 loc) 7.26 kB
import { OperationTypeNode, 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 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(); /** stores the resulting status from fetches */ 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; /** stores the original query SDL to avoid having to print */ const operationCache = fieldLevelMetricsEnabled ? lru(isHiveClient(clientOrOptions) ? 10000 : ((_b = clientOrOptions.cache) !== null && _b !== void 0 ? _b : 10000)) : null; let latestSchema = null; return { onFetch({ executionRequest }) { /** Only if the execution request is set, then this is a subgraph execution. */ if (!executionRequest) { return; } return function onFetchDone({ response }) { statusMap.set(executionRequest, response.status); }; }, onSubgraphExecute({ executionRequest, subgraphName, subgraph: subgraphSchema }) { var _a, _b, _c, _d; if (!fieldLevelMetricsEnabled) { // short circuit the entire hook to avoid processing this data. return; } const collection = (_a = executionRequest.context) === null || _a === void 0 ? void 0 : _a.__hiveUsageCollection; if (!collection) { // This is set onExecute so this should exist... but just to be safe return; } /** * Note that we need __typename on every abstract type in the subgraph call. * This is added in the "onExecute" hook to the entire document. So subgraph * calls should also include this field. */ const finishSubRequest = collection.subrequest({ subgraph: subgraphName, type: isEntityRequest(executionRequest.document) ? 'ENTITY' : 'ROOT', /** @NOTE this field's format supports batched requests, but onSubgraphExecute does not. */ paths: ((_b = executionRequest.info) === null || _b === void 0 ? void 0 : _b.path) ? [responsePathAsArray(executionRequest.info.path).join('.')] : (_d = getDefinedRootType(subgraphSchema, (_c = executionRequest.operationType) !== null && _c !== void 0 ? _c : OperationTypeNode.QUERY)) === null || _d === void 0 ? void 0 : _d.name, }); return function onSubgraphExecuteDone({ result }) { var _a; if (!isAsyncIterable(result)) { finishSubRequest({ status: (_a = statusMap.get(executionRequest)) !== null && _a !== void 0 ? _a : 200, subgraphSchema, result, document: executionRequest.document, }); } }; }, onSchemaChange({ schema }) { hive.reportSchema({ schema }); latestSchema = schema; operationCache === null || operationCache === void 0 ? void 0 : operationCache.clear(); }, onParse(parseCtx) { return ctx => { if (ctx.result.kind === 'Document' && 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 "true" is cached, then this operation doesn't need stored because it's identical to the original. // Else, the document hash been modified and cached if (cachedDocument !== true) { parseCtx.setParsedDocument(cachedDocument); } } else if (latestSchema) { const modifiedDocument = addHiveTypenames(ctx.result, latestSchema); operationCache.set(query, ctx.result === modifiedDocument || modifiedDocument); if (ctx.result !== modifiedDocument) { parseCtx.setParsedDocument(modifiedDocument); } } } }; }, onExecute({ args }) { const collection = hive.collectUsage(); // Inject the collection object into the GraphQL context // so it can be accessed downstream by subgraph executions. if (args.contextValue) { args.contextValue.__hiveUsageCollection = collection; } return { onExecuteDone({ result }) { if (!isAsyncIterable(result)) { if (result.data && fieldLevelMetricsEnabled) { hideInjectedTypenames(result.data); } void collection.finish(args, result); return; } const errors = []; return { onNext({ result }) { if (result.data && fieldLevelMetricsEnabled) { hideInjectedTypenames(result.data); } if (result.errors) { errors.push(...result.errors); } }, onEnd() { void collection.finish(args, errors.length ? { errors } : {}); }, }; }, }; }, onSubscribe({ args }) { hive.collectSubscriptionUsage({ args }); }, onDispose: async () => { await hive.dispose(); }, }; }