UNPKG

@graphql-inspector/similar-command

Version:
137 lines (136 loc) 4.9 kB
import { writeFileSync } from 'fs'; import { extname } from 'path'; import { createCommand, ensureAbsolute, parseGlobalArgs, } from '@graphql-inspector/commands'; import { similar as findSimilar, getTypePrefix } from '@graphql-inspector/core'; import { chalk, figures, Logger } from '@graphql-inspector/logger'; export function handler({ schema, writePath, type, threshold, }) { const shouldWrite = typeof writePath !== 'undefined'; const similarMap = findSimilar(schema, type, threshold); if (!Object.keys(similarMap).length) { 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 = getTypePrefix(schema.getType(typeName)); const sourceType = chalk.bold(typeName); const name = matches.bestMatch.target.typeId; Logger.log(''); Logger.log(`${prefix} ${sourceType}`); Logger.log(printResult(name, matches.bestMatch.rating)); for (const match of matches.ratings) { 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 = ensureAbsolute(writePath); const ext = extname(absPath).replace('.', '').toLocaleLowerCase(); let output = undefined; const results = transformMap(similarMap); if (ext === 'json') { output = outputJSON(results); } if (output) { writeFileSync(absPath, output, 'utf8'); Logger.success(`Available at ${absPath}\n`); } else { throw new Error(`Extension ${ext} is not supported`); } } } export default 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 } = 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 = 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 ? figures.bullet : chalk.gray(figures.bullet))) .join(''); } function formatRating(ratio) { return Math.floor(ratio * 100); }