@graphql-inspector/similar-command
Version:
Find similar types in GraphQL Schema
140 lines (139 loc) • 5.1 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.handler = handler;
const fs_1 = require("fs");
const path_1 = require("path");
const commands_1 = require("@graphql-inspector/commands");
const core_1 = require("@graphql-inspector/core");
const logger_1 = require("@graphql-inspector/logger");
function handler({ schema, writePath, type, threshold, }) {
const shouldWrite = typeof writePath !== 'undefined';
const similarMap = (0, core_1.similar)(schema, type, threshold);
if (!Object.keys(similarMap).length) {
logger_1.Logger.info('No similar types found');
return;
}
for (const typeName in similarMap) {
if (Object.prototype.hasOwnProperty.call(similarMap, typeName)) {
const matches = similarMap[typeName];
const prefix = (0, core_1.getTypePrefix)(schema.getType(typeName));
const sourceType = logger_1.chalk.bold(typeName);
const name = matches.bestMatch.target.typeId;
logger_1.Logger.log('');
logger_1.Logger.log(`${prefix} ${sourceType}`);
logger_1.Logger.log(printResult(name, matches.bestMatch.rating));
for (const match of matches.ratings) {
logger_1.Logger.log(printResult(match.target.typeId, match.rating));
}
}
}
if (shouldWrite) {
if (typeof writePath !== 'string') {
throw new Error(`--write is not valid file path: ${writePath}`);
}
const absPath = (0, commands_1.ensureAbsolute)(writePath);
const ext = (0, path_1.extname)(absPath).replace('.', '').toLocaleLowerCase();
let output = undefined;
const results = transformMap(similarMap);
if (ext === 'json') {
output = outputJSON(results);
}
if (output) {
(0, fs_1.writeFileSync)(absPath, output, 'utf8');
logger_1.Logger.success(`Available at ${absPath}\n`);
}
else {
throw new Error(`Extension ${ext} is not supported`);
}
}
}
exports.default = (0, commands_1.createCommand)(api => {
const { loaders } = api;
return {
command: 'similar <schema>',
describe: 'Find similar types in a schema',
builder(yargs) {
return yargs
.positional('schema', {
describe: 'Point to a schema',
type: 'string',
demandOption: true,
})
.options({
n: {
alias: 'name',
describe: 'Name of a type',
type: 'string',
},
t: {
alias: 'threshold',
describe: 'Threshold of similarity ratio',
type: 'number',
},
w: {
alias: 'write',
describe: 'Write a file with stats',
type: 'string',
},
});
},
async handler(args) {
const { headers, token } = (0, commands_1.parseGlobalArgs)(args);
const writePath = args.write;
const type = args.name;
const threshold = args.threshold;
const apolloFederation = args.federation || false;
const aws = args.aws || false;
const method = args.method?.toUpperCase() || 'POST';
const schema = await loaders.loadSchema(args.schema, {
headers,
token,
method,
}, apolloFederation, aws);
return handler({ schema, writePath, type, threshold });
},
};
});
function indent(line, space) {
return line.padStart(line.length + space, ' ');
}
function transformMap(similarMap) {
const results = {};
for (const typename in similarMap) {
if (Object.prototype.hasOwnProperty.call(similarMap, typename)) {
const result = similarMap[typename];
results[typename] = [];
if (result.bestMatch) {
results[typename].push(trasformResult(result.bestMatch));
}
if (result.ratings) {
results[typename].push(...result.ratings.map(trasformResult));
}
}
}
return results;
}
function trasformResult(record) {
return {
typename: record.target.typeId,
rating: record.rating,
};
}
function outputJSON(results) {
return JSON.stringify(results);
}
function printResult(name, rating) {
const percentage = logger_1.chalk.grey(`(${formatRating(rating)}%)`);
return indent(`${printScale(rating)} ${percentage} ${name}`, 0);
}
function printScale(ratio) {
const percentage = Math.floor(ratio * 100);
const levels = [0, 30, 50, 70, 90];
return levels
.map(level => percentage >= level)
.map(enabled => (enabled ? logger_1.figures.bullet : logger_1.chalk.gray(logger_1.figures.bullet)))
.join('');
}
function formatRating(ratio) {
return Math.floor(ratio * 100);
}
;