@graphql-hive/cli
Version: 
A CLI util to manage and control your GraphQL Hive
139 lines • 5.45 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.renderErrors = renderErrors;
exports.renderChanges = renderChanges;
exports.renderWarnings = renderWarnings;
exports.loadSchema = loadSchema;
exports.minifySchema = minifySchema;
const tslib_1 = require("tslib");
const colors_1 = tslib_1.__importDefault(require("colors"));
const graphql_1 = require("graphql");
const code_file_loader_1 = require("@graphql-tools/code-file-loader");
const graphql_file_loader_1 = require("@graphql-tools/graphql-file-loader");
const json_file_loader_1 = require("@graphql-tools/json-file-loader");
const load_1 = require("@graphql-tools/load");
const url_loader_1 = require("@graphql-tools/url-loader");
const base_command_1 = require("../base-command");
const gql_1 = require("../gql");
const graphql_2 = require("../gql/graphql");
const indent = '  ';
const criticalityMap = {
    [graphql_2.CriticalityLevel.Breaking]: colors_1.default.red('-'),
    [graphql_2.CriticalityLevel.Safe]: colors_1.default.green('-'),
    [graphql_2.CriticalityLevel.Dangerous]: colors_1.default.green('-'),
};
function renderErrors(errors) {
    this.fail(`Detected ${errors.total} error${errors.total > 1 ? 's' : ''}`);
    this.log('');
    errors.nodes.forEach(error => {
        this.log(String(indent), colors_1.default.red('-'), this.bolderize(error.message));
    });
}
const RenderChanges_SchemaChanges = (0, gql_1.graphql)(`
  fragment RenderChanges_schemaChanges on SchemaChangeConnection {
    total
    nodes {
      criticality
      isSafeBasedOnUsage
      message(withSafeBasedOnUsageNote: false)
      approval {
        approvedBy {
          displayName
        }
      }
    }
  }
`);
function renderChanges(maskedChanges) {
    const changes = (0, gql_1.useFragment)(RenderChanges_SchemaChanges, maskedChanges);
    const writeChanges = (changes) => {
        changes.forEach(change => {
            var _a, _b;
            const messageParts = [
                String(indent),
                criticalityMap[change.isSafeBasedOnUsage ? graphql_2.CriticalityLevel.Safe : change.criticality],
                this.bolderize(change.message),
            ];
            if (change.isSafeBasedOnUsage) {
                messageParts.push(colors_1.default.green('(Safe based on usage ✓)'));
            }
            if (change.approval) {
                messageParts.push(colors_1.default.green(`(Approved by ${(_b = (_a = change.approval.approvedBy) === null || _a === void 0 ? void 0 : _a.displayName) !== null && _b !== void 0 ? _b : '<unknown>'} ✓)`));
            }
            this.log(...messageParts);
        });
    };
    this.info(`Detected ${changes.total} change${changes.total > 1 ? 's' : ''}`);
    this.log('');
    const breakingChanges = changes.nodes.filter(change => change.criticality === graphql_2.CriticalityLevel.Breaking);
    const safeChanges = changes.nodes.filter(change => change.criticality !== graphql_2.CriticalityLevel.Breaking);
    if (breakingChanges.length) {
        this.log(String(indent), `Breaking changes:`);
        writeChanges(breakingChanges);
    }
    if (safeChanges.length) {
        this.log(String(indent), `Safe changes:`);
        writeChanges(safeChanges);
    }
}
function renderWarnings(warnings) {
    this.log('');
    this.infoWarning(`Detected ${warnings.total} warning${warnings.total > 1 ? 's' : ''}`);
    this.log('');
    warnings.nodes.forEach(warning => {
        const details = [warning.source ? `source: ${this.bolderize(warning.source)}` : undefined]
            .filter(Boolean)
            .join(', ');
        this.log(indent, `- ${this.bolderize(warning.message)}${details ? ` (${details})` : ''}`);
    });
}
async function loadSchema(
/**
 * This is used to determine the correct loader to use.
 *
 * If a user is simply introspecting a schema, the 'introspection' should be used.
 * In case of federation, we should skip the UrlLoader,
 * because it will try to introspect the schema
 * instead of fetching the SDL with directives.
 */
intent, file, options) {
    const loaders = [new code_file_loader_1.CodeFileLoader(), new graphql_file_loader_1.GraphQLFileLoader(), new json_file_loader_1.JsonFileLoader()];
    if (intent === 'federation-subgraph-introspection') {
        loaders.push(new FederationSubgraphUrlLoader());
    }
    else {
        loaders.push(new url_loader_1.UrlLoader());
    }
    const sources = await (0, load_1.loadTypedefs)(file, Object.assign(Object.assign({}, options), { cwd: process.cwd(), loaders }));
    return (0, graphql_1.print)((0, graphql_1.concatAST)(sources.map(s => s.document)));
}
function minifySchema(schema) {
    return schema.replace(/\s+/g, ' ').trim();
}
class FederationSubgraphUrlLoader {
    async load(pointer) {
        if (!pointer.startsWith('http://') && !pointer.startsWith('https://')) {
            return null;
        }
        const response = await (0, base_command_1.graphqlRequest)({
            endpoint: pointer,
        }).request({
            operation: (0, graphql_1.parse)(`
        query GetFederationSchema {
          _service {
            sdl
          }
        }
      `),
            variables: undefined,
        });
        const sdl = minifySchema(response._service.sdl);
        return [
            {
                document: (0, graphql_1.parse)(sdl),
                rawSDL: sdl,
            },
        ];
    }
}
//# sourceMappingURL=schema.js.map