UNPKG

@graphql-hive/cli

Version:

A CLI util to manage and control your GraphQL Hive

146 lines • 6.35 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const graphql_1 = require("graphql"); const core_1 = require("@oclif/core"); const base_command_1 = tslib_1.__importDefault(require("../../base-command")); const graphql_2 = require("../../gql/graphql"); const app_operations_1 = require("../../helpers/app-operations"); const config_1 = require("../../helpers/config"); const errors_1 = require("../../helpers/errors"); const TargetInput = tslib_1.__importStar(require("../../helpers/target-input")); const texture_1 = require("../../helpers/texture/texture"); function SingleOperationDefinitionRule(context) { return { Document(node) { const operations = node.definitions.filter(definition => definition.kind === graphql_1.Kind.OPERATION_DEFINITION && definition.name); if (operations.length > 1) { context.reportError(new graphql_1.GraphQLError('Multiple operation definitions found. Only one executable operation definition is allowed per document.', { nodes: operations })); } }, }; } class AppCheck extends base_command_1.default { async run() { var _a; try { const { flags, args } = await this.parse(AppCheck); let endpoint, accessToken; try { endpoint = this.ensure({ key: 'registry.endpoint', args: flags, defaultValue: config_1.graphqlEndpoint, env: 'HIVE_REGISTRY', description: AppCheck.flags['registry.endpoint'].description, }); } catch (error) { this.logDebug(error); throw new errors_1.MissingEndpointError(); } try { accessToken = this.ensure({ key: 'registry.accessToken', args: flags, env: 'HIVE_TOKEN', description: AppCheck.flags['registry.accessToken'].description, }); } catch (error) { this.logDebug(error); throw new errors_1.MissingRegistryTokenError(); } let target = null; if (flags.target) { const result = TargetInput.parse(flags.target); if (result.type === 'error') { throw new errors_1.InvalidTargetError(); } target = result.data; } const { operations, warnings } = await (0, app_operations_1.loadAppOperations)(args.operations); for (const warning of warnings) { this.warn(warning); } if (operations.length === 0) { this.logInfo('No operations found'); return; } const result = await this.registryApi(endpoint, accessToken).request({ operation: graphql_2.FetchLatestVersionDocument, variables: { target }, }); const sdl = (_a = result.latestValidVersion) === null || _a === void 0 ? void 0 : _a.sdl; if (!sdl) { throw new errors_1.SchemaNotFoundError(); } const schema = (0, graphql_1.buildSchema)(sdl, { assumeValidSDL: true, assumeValid: true, }); const invalidOperations = operations.flatMap(operation => { const source = operation.location ? `${operation.location} (${operation.operationHash})` : operation.operationHash; try { const document = (0, graphql_1.parse)(new graphql_1.Source(operation.content, source)); const errors = (0, graphql_1.validate)(schema, document, [ ...graphql_1.specifiedRules, SingleOperationDefinitionRule, ]); return errors.length > 0 ? [{ source, errors }] : []; } catch (error) { if (error instanceof graphql_1.GraphQLError) { return [{ source, errors: [error] }]; } throw error; } }); if (invalidOperations.length === 0) { this.logSuccess(`All operations are valid (${operations.length})`); return; } this.log(texture_1.Texture.header('Summary')); this.log([ `Total: ${operations.length}`, `Invalid: ${invalidOperations.length} (${Math.floor((invalidOperations.length / operations.length) * 100)}%)`, '', ].join('\n')); this.log(texture_1.Texture.header('Details')); throw new errors_1.InvalidDocumentsError(invalidOperations); } catch (error) { if (error instanceof core_1.Errors.CLIError) { throw error; } this.logFailure('Failed to validate operations'); throw new errors_1.UnexpectedError(error); } } } AppCheck.description = 'checks app operations against the latest published schema'; AppCheck.flags = { 'registry.endpoint': core_1.Flags.string({ description: 'registry endpoint', }), 'registry.accessToken': core_1.Flags.string({ description: 'registry access token', }), target: core_1.Flags.string({ description: 'The target against which the app operations are checked.' + ' This can either be a slug following the format "$organizationSlug/$projectSlug/$targetSlug" (e.g "the-guild/graphql-hive/staging")' + ' or an UUID (e.g. "a0f4c605-6541-4350-8cfe-b31f21a4bf80").', }), }; AppCheck.args = { operations: core_1.Args.string({ name: 'operations', required: true, description: 'Path to the persisted operations manifest (GraphQL Code Generator, Relay or Apollo persisted query manifest JSON file), a directory containing .graphql files, or a glob pattern matching .graphql files.', hidden: false, }), }; exports.default = AppCheck; //# sourceMappingURL=check.js.map