@graphql-hive/cli
Version:
A CLI util to manage and control your GraphQL Hive
146 lines • 6.35 kB
JavaScript
;
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