@graphql-inspector/core
Version:
Tooling for GraphQL. Compare GraphQL Schemas, check documents, find breaking changes, find similar types.
160 lines (159 loc) • 6.36 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.coverage = coverage;
const graphql_1 = require("graphql");
const document_js_1 = require("../ast/document.js");
const graphql_js_1 = require("../utils/graphql.js");
function coverage(schema, sources) {
const coverage = {
sources,
types: {},
stats: {
numTypes: 0,
numTypesCoveredFully: 0,
numTypesCovered: 0,
numFields: 0,
numFieldsCovered: 0,
numFiledsCovered: 0,
numQueries: 0,
numCoveredQueries: 0,
numMutations: 0,
numCoveredMutations: 0,
numSubscriptions: 0,
numCoveredSubscriptions: 0,
},
};
const typeMap = schema.getTypeMap();
const typeInfo = new graphql_1.TypeInfo(schema);
const visitor = source => ({
Field(node) {
const fieldDef = typeInfo.getFieldDef();
const parent = typeInfo.getParentType();
if (parent?.name &&
!(0, graphql_js_1.isForIntrospection)(parent.name) &&
fieldDef?.name &&
fieldDef.name !== '__typename' &&
fieldDef.name !== '__schema') {
const sourceName = source.name;
const typeCoverage = coverage.types[parent.name];
const fieldCoverage = typeCoverage.children[fieldDef.name];
const locations = fieldCoverage.locations[sourceName];
switch (typeCoverage.type.name) {
case 'Query':
coverage.stats.numCoveredQueries++;
break;
case 'Mutation':
coverage.stats.numCoveredMutations++;
break;
case 'Subscription':
coverage.stats.numCoveredSubscriptions++;
break;
}
typeCoverage.hits++;
fieldCoverage.hits++;
if (node.loc) {
fieldCoverage.locations[sourceName] = [node.loc, ...(locations || [])];
}
if (node.arguments) {
for (const argNode of node.arguments) {
const argCoverage = fieldCoverage.children[argNode.name.value];
argCoverage.hits++;
if (argNode.loc) {
argCoverage.locations[sourceName] = [
argNode.loc,
...(argCoverage.locations[sourceName] || []),
];
}
}
}
}
},
});
for (const typename in typeMap) {
if (!(0, graphql_js_1.isForIntrospection)(typename) && !(0, graphql_js_1.isPrimitive)(typename)) {
const type = typeMap[typename];
if ((0, graphql_1.isObjectType)(type) || (0, graphql_1.isInterfaceType)(type)) {
const typeCoverage = {
hits: 0,
fieldsCount: 0,
fieldsCountCovered: 0,
type,
children: {},
};
const fieldMap = type.getFields();
for (const fieldname in fieldMap) {
if ((0, graphql_1.isObjectType)(type) || (0, graphql_1.isInterfaceType)(type)) {
switch (type.name) {
case 'Query':
coverage.stats.numQueries++;
break;
case 'Mutation':
coverage.stats.numMutations++;
break;
case 'Subscription':
coverage.stats.numSubscriptions++;
break;
}
}
const field = fieldMap[fieldname];
typeCoverage.children[field.name] = {
hits: 0,
fieldsCount: 0,
fieldsCountCovered: 0,
locations: {},
children: {},
};
for (const arg of field.args) {
typeCoverage.children[field.name].children[arg.name] = {
hits: 0,
fieldsCount: 0,
fieldsCountCovered: 0,
locations: {},
};
}
}
coverage.types[type.name] = typeCoverage;
}
}
}
const documents = coverage.sources.map(document_js_1.readDocument);
for (const [i, doc] of documents.entries()) {
const source = coverage.sources[i];
for (const op of doc.operations) {
(0, graphql_1.visit)(op.node, (0, graphql_1.visitWithTypeInfo)(typeInfo, visitor(source)));
}
for (const fr of doc.fragments) {
(0, graphql_1.visit)(fr.node, (0, graphql_1.visitWithTypeInfo)(typeInfo, visitor(source)));
}
}
for (const key in coverage.types) {
const me = coverage.types[key];
processStats(me);
coverage.stats.numTypes++;
if (me.fieldsCountCovered > 0)
coverage.stats.numTypesCovered++;
if (me.fieldsCount === me.fieldsCountCovered)
coverage.stats.numTypesCoveredFully++;
coverage.stats.numFields += me.fieldsCount;
coverage.stats.numFieldsCovered += me.fieldsCountCovered;
coverage.stats.numFiledsCovered = coverage.stats.numFieldsCovered;
}
return coverage;
}
function processStats(me) {
const children = me.children;
if (children) {
for (const k in children) {
const ch = children[k];
if (ch.children !== undefined) {
processStats(ch);
me.fieldsCount += ch.fieldsCount;
me.fieldsCountCovered += ch.fieldsCountCovered;
}
me.fieldsCount++;
if (ch.hits > 0) {
me.fieldsCountCovered++;
}
}
}
}