@graphql-hive/gateway-plugin-console-sdk
Version:
GraphQL Hive + GraphQL Hive Gateway
140 lines (139 loc) • 6.74 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createHive = createHive;
exports.useHive = useHive;
const graphql_1 = require("graphql");
const tiny_lru_1 = require("tiny-lru");
const core_1 = require("@graphql-hive/core");
const add_typenames_js_1 = require("./add-typenames.js");
const is_entity_request_js_1 = require("./is-entity-request.js");
const version_js_1 = require("./version.js");
function createHive(clientOrOptions) {
return (0, core_1.createHive)(Object.assign(Object.assign({}, clientOrOptions), { agent: Object.assign({ name: 'hive-client-gateway-sdk', version: version_js_1.version }, clientOrOptions.agent) }));
}
function useHive(clientOrOptions) {
var _a, _b;
const hive = (0, core_1.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();
/** stores the original query SDL to avoid having to print */
const operationCache = (0, tiny_lru_1.lru)((0, core_1.isHiveClient)(clientOrOptions) ? 10000 : ((_a = clientOrOptions.cache) !== null && _a !== void 0 ? _a : 10000));
if (hive[core_1.autoDisposeSymbol]) {
if (global.process) {
const signals = Array.isArray(hive[core_1.autoDisposeSymbol])
? hive[core_1.autoDisposeSymbol]
: ['SIGINT', 'SIGTERM'];
for (const signal of signals) {
process.once(signal, () => hive.dispose());
}
}
else {
console.error('It seems that GraphQL Hive is not being executed in Node.js. ' +
'Please attempt manual client disposal and use autoDispose: false option.');
}
}
const fieldLevelMetricsEnabled = (0, core_1.isHiveClient)(clientOrOptions)
? false
: ((_b = clientOrOptions.fieldLevelMetricsEnabled) !== null && _b !== void 0 ? _b : false);
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;
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: (0, is_entity_request_js_1.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)
? [(0, graphql_1.responsePathAsArray)(executionRequest.info.path).join('.')]
: [],
});
return function onSubgraphExecuteDone({ result }) {
var _a;
if (!(0, core_1.isAsyncIterable)(result)) {
finishSubRequest({
status: (_a = statusMap.get(executionRequest)) !== null && _a !== void 0 ? _a : 200,
subgraphSchema,
result,
document: executionRequest.document,
});
}
};
},
onSchemaChange({ schema }) {
hive.reportSchema({ schema });
},
onExecute({ args }) {
var _a;
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;
}
// 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 = (_a = args.contextValue.params.query) !== null && _a !== void 0 ? _a : (0, graphql_1.print)(args.document);
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) {
args.document = cachedDocument;
}
}
else {
const modifiedDocument = (0, add_typenames_js_1.addTypenames)(args.document, args.schema);
operationCache.set(query, args.document === modifiedDocument || args.document);
}
return {
onExecuteDone({ result }) {
if (!(0, core_1.isAsyncIterable)(result)) {
void collection.finish(args, result);
return;
}
const errors = [];
return {
onNext(ctx) {
if (ctx.result.errors) {
errors.push(...ctx.result.errors);
}
},
onEnd() {
void collection.finish(args, errors.length ? { errors } : {});
},
};
},
};
},
onSubscribe({ args }) {
hive.collectSubscriptionUsage({ args });
},
};
}