@graphql-inspector/action
Version:
GraphQL Inspector functionality for GitHub Actions
186 lines (185 loc) • 8.85 kB
JavaScript
import { Kind, parseConstValue, parseType, print, } from 'graphql';
import { AddedAttributeCoordinateNotFoundError, AddedCoordinateAlreadyExistsError, ChangedAncestorCoordinateNotFoundError, ChangedCoordinateKindMismatchError, ChangePathMissingError, DeletedAncestorCoordinateNotFoundError, DeletedAttributeNotFoundError, DeletedCoordinateNotFound, ValueMismatchError, } from '../errors.js';
import { nameNode, stringNode } from '../node-templates.js';
import { assertValueMatch, getChangedNodeOfKind, getDeletedNodeOfKind, parentPath, } from '../utils.js';
export function fieldTypeChanged(change, nodeByPath, config, _context) {
const node = getChangedNodeOfKind(change, nodeByPath, Kind.FIELD_DEFINITION, config);
if (node) {
const currentReturnType = print(node.type);
if (change.meta.oldFieldType !== currentReturnType) {
config.onError(new ValueMismatchError(Kind.FIELD_DEFINITION, change.meta.oldFieldType, currentReturnType), change);
}
node.type = parseType(change.meta.newFieldType);
}
}
export function fieldRemoved(change, nodeByPath, config, _context) {
if (!change.path) {
config.onError(new ChangePathMissingError(change), change);
return;
}
const typeNode = nodeByPath.get(parentPath(change.path));
if (!typeNode) {
config.onError(new DeletedAncestorCoordinateNotFoundError(change.path, change.type, change.meta.removedFieldName), change);
return;
}
const beforeLength = typeNode.fields?.length ?? 0;
typeNode.fields = typeNode.fields?.filter(f => f.name.value !== change.meta.removedFieldName);
if (beforeLength === (typeNode.fields?.length ?? 0)) {
config.onError(new DeletedAttributeNotFoundError(change.path, change.type, 'fields', change.meta.removedFieldName), change);
}
else {
// delete the reference to the removed field.
nodeByPath.delete(change.path);
}
}
export function fieldAdded(change, nodeByPath, config, _context) {
if (!change.path) {
config.onError(new ChangePathMissingError(change), change);
return;
}
const changedNode = nodeByPath.get(change.path);
if (changedNode) {
if (changedNode.kind === Kind.FIELD_DEFINITION) {
if (print(changedNode.type) === change.meta.addedFieldReturnType) {
config.onError(new AddedCoordinateAlreadyExistsError(change.path, change.type), change);
}
else {
config.onError(new ValueMismatchError(Kind.FIELD_DEFINITION, undefined, change.meta.addedFieldReturnType), change);
}
}
else {
config.onError(new ChangedCoordinateKindMismatchError(Kind.FIELD_DEFINITION, changedNode.kind), change);
}
return;
}
const typeNode = nodeByPath.get(parentPath(change.path));
if (!typeNode) {
config.onError(new ChangedAncestorCoordinateNotFoundError(change.path, change.type, change.meta.addedFieldName), change);
return;
}
if (typeNode.kind !== Kind.OBJECT_TYPE_DEFINITION &&
typeNode.kind !== Kind.INTERFACE_TYPE_DEFINITION) {
config.onError(new ChangedCoordinateKindMismatchError(Kind.ENUM_TYPE_DEFINITION, typeNode.kind), change);
return;
}
const node = {
kind: Kind.FIELD_DEFINITION,
name: nameNode(change.meta.addedFieldName),
type: parseType(change.meta.addedFieldReturnType),
// description: change.meta.addedFieldDescription
// ? stringNode(change.meta.addedFieldDescription)
// : undefined,
};
typeNode.fields = [...(typeNode.fields ?? []), node];
// add new field to the node set
nodeByPath.set(change.path, node);
}
export function fieldArgumentAdded(change, nodeByPath, config, _context) {
if (!change.path) {
config.onError(new ChangePathMissingError(change), change);
return;
}
const existing = nodeByPath.get(change.path);
if (existing) {
config.onError(new AddedCoordinateAlreadyExistsError(change.path, change.type), change);
return;
}
const fieldNode = nodeByPath.get(parentPath(change.path));
if (!fieldNode) {
config.onError(new AddedAttributeCoordinateNotFoundError(change.path, change.type, change.meta.addedArgumentName), change);
return;
}
if (fieldNode.kind !== Kind.FIELD_DEFINITION) {
config.onError(new ChangedCoordinateKindMismatchError(Kind.FIELD_DEFINITION, fieldNode.kind), change);
return;
}
const node = {
kind: Kind.INPUT_VALUE_DEFINITION,
name: nameNode(change.meta.addedArgumentName),
type: parseType(change.meta.addedArgumentType),
// description: change.meta.addedArgumentDescription
// ? stringNode(change.meta.addedArgumentDescription)
// : undefined,
};
fieldNode.arguments = [...(fieldNode.arguments ?? []), node];
// add new field to the node set
nodeByPath.set(change.path, node);
}
export function fieldArgumentTypeChanged(change, nodeByPath, config, _context) {
const existingArg = getChangedNodeOfKind(change, nodeByPath, Kind.INPUT_VALUE_DEFINITION, config);
if (existingArg) {
assertValueMatch(change, Kind.INPUT_VALUE_DEFINITION, change.meta.oldArgumentType, print(existingArg.type), config);
existingArg.type = parseType(change.meta.newArgumentType);
}
}
export function fieldArgumentDescriptionChanged(change, nodeByPath, config, _context) {
const existingArg = getChangedNodeOfKind(change, nodeByPath, Kind.INPUT_VALUE_DEFINITION, config);
if (existingArg) {
assertValueMatch(change, Kind.INPUT_VALUE_DEFINITION, change.meta.oldDescription ?? undefined, existingArg.description?.value, config);
existingArg.description = change.meta.newDescription
? stringNode(change.meta.newDescription)
: undefined;
}
}
export function fieldArgumentDefaultChanged(change, nodeByPath, config, _context) {
const existingArg = getChangedNodeOfKind(change, nodeByPath, Kind.INPUT_VALUE_DEFINITION, config);
if (existingArg) {
assertValueMatch(change, Kind.INPUT_VALUE_DEFINITION, change.meta.oldDefaultValue, existingArg.defaultValue && print(existingArg.defaultValue), config);
existingArg.defaultValue = change.meta.newDefaultValue
? parseConstValue(change.meta.newDefaultValue)
: undefined;
}
}
export function fieldArgumentRemoved(change, nodeByPath, config, _context) {
const existing = getDeletedNodeOfKind(change, nodeByPath, Kind.ARGUMENT, config);
if (!existing) {
config.onError(new DeletedCoordinateNotFound(change.path ?? '', change.type), change);
return;
}
const fieldNode = nodeByPath.get(parentPath(change.path));
if (!fieldNode) {
config.onError(new DeletedAncestorCoordinateNotFoundError(change.path, // asserted by "getDeletedNodeOfKind"
change.type, change.meta.removedFieldArgumentName), change);
return;
}
if (fieldNode.kind !== Kind.FIELD_DEFINITION) {
config.onError(new ChangedCoordinateKindMismatchError(Kind.FIELD_DEFINITION, fieldNode.kind), change);
}
fieldNode.arguments = fieldNode.arguments?.filter(a => a.name.value === change.meta.removedFieldArgumentName);
// add new field to the node set
nodeByPath.delete(change.path);
}
export function fieldDescriptionAdded(change, nodeByPath, config, _context) {
const fieldNode = getChangedNodeOfKind(change, nodeByPath, Kind.FIELD_DEFINITION, config);
if (fieldNode) {
fieldNode.description = change.meta.addedDescription
? stringNode(change.meta.addedDescription)
: undefined;
}
}
export function fieldDescriptionRemoved(change, nodeByPath, config, _context) {
if (!change.path) {
config.onError(new ChangePathMissingError(change), change);
return;
}
const fieldNode = nodeByPath.get(change.path);
if (!fieldNode) {
config.onError(new DeletedCoordinateNotFound(change.path, change.type), change);
return;
}
if (fieldNode.kind !== Kind.FIELD_DEFINITION) {
config.onError(new ChangedCoordinateKindMismatchError(Kind.FIELD_DEFINITION, fieldNode.kind), change);
return;
}
fieldNode.description = undefined;
}
export function fieldDescriptionChanged(change, nodeByPath, config, _context) {
const fieldNode = getChangedNodeOfKind(change, nodeByPath, Kind.FIELD_DEFINITION, config);
if (!fieldNode) {
return;
}
if (fieldNode.description?.value !== change.meta.oldDescription) {
config.onError(new ValueMismatchError(Kind.FIELD_DEFINITION, change.meta.oldDescription, fieldNode.description?.value), change);
}
fieldNode.description = stringNode(change.meta.newDescription);
}