UNPKG

wgc

Version:

The official CLI tool to manage the GraphQL Federation Platform Cosmo

244 lines 13.6 kB
import { EnumStatusCode } from '@wundergraph/cosmo-connect/dist/common/common_pb'; import Table from 'cli-table3'; import { program } from 'commander'; import logSymbols from 'log-symbols'; import pc from 'picocolors'; import { config } from './core/config.js'; export const handleCheckResult = (resp) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j; const changesTable = new Table({ head: [pc.bold(pc.white('CHANGE')), pc.bold(pc.white('TYPE')), pc.bold(pc.white('DESCRIPTION'))], wordWrap: true, }); const compositionErrorsTable = new Table({ head: [pc.bold(pc.white('GRAPH_NAME')), pc.bold(pc.white('NAMESPACE')), pc.bold(pc.white('ERROR_MESSAGE'))], colWidths: [30, 30, 120], wordWrap: true, }); const compositionWarningsTable = new Table({ head: [pc.bold(pc.white('GRAPH_NAME')), pc.bold(pc.white('NAMESPACE')), pc.bold(pc.white('WARNING_MESSAGE'))], colWidths: [30, 30, 120], wordWrap: true, }); const lintIssuesTable = new Table({ head: [pc.bold(pc.white('LINT_RULE')), pc.bold(pc.white('ERROR_MESSAGE')), pc.bold(pc.white('LINE NUMBER'))], colAligns: ['left', 'left', 'center'], wordWrap: true, }); const graphPruningIssuesTable = new Table({ head: [ pc.bold(pc.white('RULE')), pc.bold(pc.white('FEDERATED_GRAPH_NAME')), pc.bold(pc.white('FIELD_PATH')), pc.bold(pc.white('MESSAGE')), pc.bold(pc.white('LINE NUMBER')), ], colAligns: ['left', 'left', 'left', 'left', 'center'], wordWrap: true, }); let success = false; let finalStatement = ''; let studioCheckDestination = ''; if (resp.checkId && resp.checkedFederatedGraphs.length > 0) { studioCheckDestination = `${pc.bold('Open in studio')}: ${config.webURL}/${resp.checkedFederatedGraphs[0].organizationSlug}/${resp.checkedFederatedGraphs[0].namespace}/graph/${resp.checkedFederatedGraphs[0].name}/checks/${resp.checkId}`; } switch ((_a = resp.response) === null || _a === void 0 ? void 0 : _a.code) { case EnumStatusCode.OK: { if (resp.proposalMatchMessage) { console.log(pc.yellow(`Warning: Proposal match failed`)); console.log(pc.yellow(resp.proposalMatchMessage)); } if (resp.nonBreakingChanges.length === 0 && resp.breakingChanges.length === 0 && resp.compositionErrors.length === 0 && resp.lintErrors.length === 0 && resp.lintWarnings.length === 0 && resp.graphPruneErrors.length === 0 && resp.graphPruneWarnings.length === 0) { console.log(`\nDetected no changes.\nDetected no lint issues.\nDetected no graph pruning issues.\n\n${studioCheckDestination}\n`); success = true; break; } console.log(`\nChecking the proposed schema`); // No operations usage stats mean the check was not performed against any live traffic if (resp.operationUsageStats) { if (resp.operationUsageStats.totalOperations === 0 && !resp.clientTrafficCheckSkipped) { // Composition errors are still considered failures, otherwise we can consider this a success // because no operations were affected by the change success = resp.compositionErrors.length === 0 && resp.lintErrors.length === 0 && resp.graphPruneErrors.length === 0; console.log(`No operations were affected by this schema change.`); finalStatement = `This schema change didn't affect any operations from existing client traffic.`; } else if (resp.operationUsageStats.totalOperations === resp.operationUsageStats.safeOperations && !resp.clientTrafficCheckSkipped) { // This is also a success because changes to these operations were marked as safe success = resp.compositionErrors.length === 0 && resp.lintErrors.length === 0 && resp.graphPruneErrors.length === 0; console.log(`${resp.operationUsageStats.totalOperations} operations were considered safe due to overrides.`); finalStatement = `This schema change affected operations with safe overrides.`; } else { // Composition and breaking errors are considered failures because operations were affected by the change success = resp.breakingChanges.length === 0 && resp.compositionErrors.length === 0 && resp.lintErrors.length === 0 && resp.graphPruneErrors.length === 0; const { breakingChanges, operationUsageStats, clientTrafficCheckSkipped } = resp; const { totalOperations, safeOperations, firstSeenAt, lastSeenAt } = operationUsageStats; if (breakingChanges.length > 0) { const warningMessage = [logSymbols.warning, ` Found ${pc.bold(breakingChanges.length)} breaking changes.`]; if (totalOperations > 0) { warningMessage.push(`${pc.bold(totalOperations - safeOperations)} operations impacted.`); } if (safeOperations > 0) { warningMessage.push(`In addition, ${safeOperations} operations marked safe due to overrides.`); } if (!clientTrafficCheckSkipped) { warningMessage.push(`\nFound client activity between ${pc.underline(new Date(firstSeenAt).toLocaleString())} and ${pc.underline(new Date(lastSeenAt).toLocaleString())}.`); } console.log(warningMessage.join('')); finalStatement = `This check has encountered ${pc.bold(`${breakingChanges.length}`)} breaking changes${clientTrafficCheckSkipped ? `.` : ` that would break operations from existing client traffic.`}`; } } } if (resp.nonBreakingChanges.length > 0 || resp.breakingChanges.length > 0) { console.log('\nDetected the following changes:'); if (resp.breakingChanges.length > 0) { for (const breakingChange of resp.breakingChanges) { changesTable.push([ `${logSymbols.error} ${pc.red('BREAKING')}`, breakingChange.changeType, breakingChange.message, ]); } } if (resp.nonBreakingChanges.length > 0) { for (const nonBreakingChange of resp.nonBreakingChanges) { changesTable.push([ `${logSymbols.success} NON-BREAKING`, nonBreakingChange.changeType, nonBreakingChange.message, ]); } } console.log(changesTable.toString()); } if (resp.compositionErrors.length > 0) { console.log(pc.red('\nDetected composition errors:')); for (const compositionError of resp.compositionErrors) { compositionErrorsTable.push([ compositionError.federatedGraphName, compositionError.namespace, compositionError.message, ]); } console.log(compositionErrorsTable.toString()); } if (resp.compositionWarnings.length > 0) { console.log(pc.yellow(`\nDetected composition warnings:`)); for (const compositionWarning of resp.compositionWarnings) { compositionWarningsTable.push([ compositionWarning.federatedGraphName, compositionWarning.namespace, compositionWarning.message, ]); } console.log(compositionWarningsTable.toString()); } if (resp.lintErrors.length > 0 || resp.lintWarnings.length > 0) { console.log('\nDetected lint issues:'); for (const error of resp.lintErrors) { lintIssuesTable.push([ `${logSymbols.error} ${pc.red(error.lintRuleType)}`, error.message, (_b = error.issueLocation) === null || _b === void 0 ? void 0 : _b.line, ]); } for (const warning of resp.lintWarnings) { lintIssuesTable.push([ `${logSymbols.warning} ${pc.yellow(warning.lintRuleType)}`, warning.message, (_c = warning.issueLocation) === null || _c === void 0 ? void 0 : _c.line, ]); } console.log(lintIssuesTable.toString()); } if (resp.graphPruneErrors.length > 0 || resp.graphPruneWarnings.length > 0) { console.log('\nDetected graph pruning issues:'); for (const error of resp.graphPruneErrors) { graphPruningIssuesTable.push([ `${logSymbols.error} ${pc.red(error.graphPruningRuleType)}`, error.federatedGraphName, error.fieldPath, error.message, ((_d = error.issueLocation) === null || _d === void 0 ? void 0 : _d.line) || '-', ]); } for (const warning of resp.graphPruneWarnings) { graphPruningIssuesTable.push([ `${logSymbols.warning} ${pc.yellow(warning.graphPruningRuleType)}`, warning.federatedGraphName, warning.fieldPath, warning.message, ((_e = warning.issueLocation) === null || _e === void 0 ? void 0 : _e.line) || '-', ]); } console.log(graphPruningIssuesTable.toString()); } if (resp.isLinkedTrafficCheckFailed || resp.isLinkedPruningCheckFailed) { finalStatement += success ? `\n\n But this schema change has been linked to a target subgraph and the target subgraph check has failed.` : `\n\n This schema change has been linked to a target subgraph and the target subgraph check has failed.`; if (resp.isLinkedTrafficCheckFailed) { finalStatement += `\n\n The target subgraph check has failed because of client traffic issues.`; } if (resp.isLinkedPruningCheckFailed) { finalStatement += `\n\n The target subgraph check has failed because of graph pruning issues.`; } success = false; } if (success) { console.log('\n' + logSymbols.success + pc.green(` Schema check passed. ${finalStatement}`) + '\n\n' + studioCheckDestination + '\n'); } else { program.error('\n' + logSymbols.error + pc.red(` Schema check failed. ${finalStatement}\nSee https://cosmo-docs.wundergraph.com/studio/schema-checks for more information on resolving operation check errors.\n${studioCheckDestination}\n`) + '\n'); } break; } case EnumStatusCode.ERR_SCHEMA_MISMATCH_WITH_APPROVED_PROPOSAL: { console.log(pc.red(`Error: Proposal match failed`)); console.log(pc.red(resp.proposalMatchMessage)); console.log(logSymbols.error + pc.red(`Schema check failed.\nSee https://cosmo-docs.wundergraph.com/studio/schema-checks for more information on resolving operation check errors.\n${studioCheckDestination}\n`)); success = false; break; } case EnumStatusCode.ERR_INVALID_SUBGRAPH_SCHEMA: { console.log('\nCheck has failed early because the schema could not be built. Please ensure that the schema is valid GraphQL and try again.'); if ((_f = resp.response) === null || _f === void 0 ? void 0 : _f.details) { console.log(pc.red(pc.bold((_g = resp.response) === null || _g === void 0 ? void 0 : _g.details))); } program.error(logSymbols.error + pc.red(' Schema check failed.')); break; } default: { console.log('\nFailed to perform the check operation.'); if ((_h = resp.response) === null || _h === void 0 ? void 0 : _h.details) { console.log(pc.red(pc.bold((_j = resp.response) === null || _j === void 0 ? void 0 : _j.details))); } program.error(logSymbols.error + pc.red(' Schema check failed.')); } } return success; }; //# sourceMappingURL=handle-check-result.js.map