Skip to content

Commit

Permalink
Cut down on nesting in pre-processing
Browse files Browse the repository at this point in the history
  • Loading branch information
marcvberg committed Jun 10, 2022
1 parent 0e017e2 commit 2646fc1
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,44 +67,32 @@ export class BelongsToTransformer extends TransformerPluginBase {
const resultDoc: DocumentNode = produce(context.inputDocument, draftDoc => {
const objectTypeMap = new Map<string, WritableDraft<ObjectTypeDefinitionNode | ObjectTypeExtensionNode>>(); // key: type name | value: object type node
// First iteration builds a map of the object types to reference for relation types
draftDoc?.definitions?.forEach(def => {
if (def.kind === 'ObjectTypeExtension' || def.kind === 'ObjectTypeDefinition') {
objectTypeMap.set(def.name.value, def);
}
});

draftDoc?.definitions?.forEach(def => {
if (def.kind === 'ObjectTypeExtension' || def.kind === 'ObjectTypeDefinition') {
def?.fields?.forEach(field => {
field?.directives?.forEach(dir => {
if (dir.name.value === directiveName) {
const relatedType = objectTypeMap.get(getBaseType(field.type));
if (relatedType) { // Validation is done in a different segment of the life cycle
const relationTypeField = relatedType?.fields?.find(relatedField => {
if (getBaseType(relatedField.type) === def.name.value && relatedField?.directives?.some(
relatedDir => relatedDir.name.value === 'hasOne' || relatedDir.name.value === 'hasMany',
)) {
return true;
}
return false;
});
const relationTypeName = relationTypeField?.directives?.find(relationDir => relationDir.name.value === 'hasOne' || relationDir.name.value === 'hasMany')?.name?.value;
if (relationTypeName === 'hasOne') {
const connectionAttributeName = getConnectionAttributeName(def.name.value, field.name.value);
if (!def?.fields?.some(defField => defField.name.value === connectionAttributeName)) {
def?.fields?.push(
makeField(
connectionAttributeName, [], isNonNullType(field.type) ?
makeNonNullType(makeNamedType('ID')) : makeNamedType('ID'), [],
) as WritableDraft<FieldDefinitionNode>,
);
}
}
}
}
});
});
}
const filteredDefs = draftDoc?.definitions?.filter(def => def.kind === 'ObjectTypeExtension' || def.kind === 'ObjectTypeDefinition');
const objectDefs = filteredDefs as Array<WritableDraft<ObjectTypeDefinitionNode | ObjectTypeExtensionNode>>;
objectDefs?.forEach(def => objectTypeMap.set(def.name.value, def));

objectDefs?.forEach(def => {
const filteredFields = def?.fields?.filter(field => field?.directives?.some(dir => dir.name.value === directiveName && objectTypeMap.get(getBaseType(field.type))));
filteredFields?.forEach(field => {
const relatedType = objectTypeMap.get(getBaseType(field.type));
const relationTypeField = relatedType?.fields?.find(relatedField =>
getBaseType(relatedField.type) === def.name.value &&
relatedField?.directives?.some(relatedDir => relatedDir.name.value === 'hasOne' || relatedDir.name.value === 'hasMany')
);
const relationTypeName = relationTypeField?.directives?.find(relationDir => relationDir.name.value === 'hasOne' || relationDir.name.value === 'hasMany')?.name?.value;

if (relationTypeName === 'hasOne') {
const connectionAttributeName = getConnectionAttributeName(def.name.value, field.name.value);
if (!def?.fields?.some(defField => defField.name.value === connectionAttributeName)) {
def?.fields?.push(
makeField(
connectionAttributeName, [], isNonNullType(field.type) ?
makeNonNullType(makeNamedType('ID')) : makeNamedType('ID'), [],
) as WritableDraft<FieldDefinitionNode>,
);
}
}
});
});
});
return resultDoc;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
FieldDefinitionNode,
InterfaceTypeDefinitionNode,
NamedTypeNode,
ObjectTypeDefinitionNode,
ObjectTypeDefinitionNode, ObjectTypeExtensionNode,
} from 'graphql';
import { makeQueryConnectionWithKeyResolver, updateTableForConnection } from './resolvers';
import { ensureHasManyConnectionField, extendTypeWithConnection } from './schema';
Expand Down Expand Up @@ -69,28 +69,23 @@ export class HasManyTransformer extends TransformerPluginBase {
mutateSchema = (context: TransformerPreProcessContextProvider): DocumentNode => {
const resultDoc: DocumentNode = produce(context.inputDocument, draftDoc => {
const connectingFieldsMap = new Map<string, WritableDraft<FieldDefinitionNode>>(); // key: type name | value: connecting field
const filteredDefs = draftDoc?.definitions?.filter(def => def.kind === 'ObjectTypeDefinition' || def.kind === 'ObjectTypeExtension');
const objectDefs = filteredDefs as Array<WritableDraft<ObjectTypeDefinitionNode | ObjectTypeExtensionNode>>;
// First iteration builds a map of the hasMany connecting fields that need to exist, second iteration ensures they exist
draftDoc?.definitions?.forEach(def => {
if (def.kind === 'ObjectTypeExtension' || def.kind === 'ObjectTypeDefinition') {
def?.fields?.forEach(field => {
field?.directives?.forEach(dir => {
if (dir.name.value === directiveName) {
const baseFieldType = getBaseType(field.type);
const connectionAttributeName = getConnectionAttributeName(def.name.value, field.name.value);
const newField = makeField(connectionAttributeName, [], isNonNullType(field.type) ? makeNonNullType(makeNamedType('ID')) : makeNamedType('ID'), []);
connectingFieldsMap.set(baseFieldType, newField as WritableDraft<FieldDefinitionNode>);
}
});
});
}
objectDefs?.forEach(def => {
const filteredFields = def?.fields?.filter(field => field?.directives?.some(dir => dir.name.value === directiveName));
filteredFields?.forEach(field => {
const baseFieldType = getBaseType(field.type);
const connectionAttributeName = getConnectionAttributeName(def.name.value, field.name.value);
const newField = makeField(connectionAttributeName, [], isNonNullType(field.type) ? makeNonNullType(makeNamedType('ID')) : makeNamedType('ID'), []);
connectingFieldsMap.set(baseFieldType, newField as WritableDraft<FieldDefinitionNode>);
});
});

draftDoc?.definitions?.forEach(def => {
if ((def.kind === 'ObjectTypeDefinition' || def.kind === 'ObjectTypeExtension') && connectingFieldsMap.has(def.name.value)) {
const fieldToAdd = connectingFieldsMap.get(def.name.value);
if (def.fields && fieldToAdd && !def.fields.some(field => field.name.value === fieldToAdd.name.value)) {
def.fields.push(fieldToAdd);
}
objectDefs?.filter(def => connectingFieldsMap.has(def.name.value))?.forEach(def => {
const fieldToAdd = connectingFieldsMap.get(def.name.value);
if (def.fields && fieldToAdd && !def.fields.some(field => field.name.value === fieldToAdd.name.value)) {
def.fields.push(fieldToAdd);
}
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
DocumentNode,
FieldDefinitionNode,
InterfaceTypeDefinitionNode,
ObjectTypeDefinitionNode,
ObjectTypeDefinitionNode, ObjectTypeExtensionNode,
} from 'graphql';
import {
isListType,
Expand Down Expand Up @@ -74,40 +74,40 @@ export class HasOneTransformer extends TransformerPluginBase {
*/
mutateSchema = (context: TransformerPreProcessContextProvider): DocumentNode => {
const document: DocumentNode = produce(context.inputDocument, draftDoc => {
draftDoc.definitions.forEach(def => {
if (def.kind === 'ObjectTypeDefinition' || def.kind === 'ObjectTypeExtension') {
def?.fields?.forEach(field => {
field?.directives?.forEach(dir => {
if (dir.name.value === directiveName) {
const connectionAttributeName = getConnectionAttributeName(def.name.value, field.name.value);
let hasFieldsDefined = false;
let removalIndex = -1;
dir?.arguments?.forEach((arg, idx) => {
if (arg.name.value === 'fields') {
if ((arg.value.kind === 'StringValue' && arg.value.value) || (arg.value.kind === 'ListValue' && arg.value.values && arg.value.values.length > 0)) {
hasFieldsDefined = true;
} else {
removalIndex = idx;
}
}
});
if (removalIndex !== -1) {
dir?.arguments?.splice(removalIndex, 1);
}
if (!hasFieldsDefined) {
// eslint-disable-next-line no-param-reassign
dir.arguments = [makeArgument('fields', makeValueNode(connectionAttributeName)) as WritableDraft<ArgumentNode>];
def?.fields?.push(
makeField(
connectionAttributeName, [], isNonNullType(field.type) ?
makeNonNullType(makeNamedType('ID')) : makeNamedType('ID'), [],
) as WritableDraft<FieldDefinitionNode>,
);
const filteredDefs = draftDoc?.definitions?.filter(def => def.kind === 'ObjectTypeDefinition' || def.kind === 'ObjectTypeExtension');
const objectDefs = filteredDefs as Array<WritableDraft<ObjectTypeDefinitionNode | ObjectTypeExtensionNode>>;

objectDefs?.forEach(def => {
const filteredFields = def?.fields?.filter(field => field?.directives?.some(dir => dir.name.value === directiveName));
filteredFields?.forEach(field => {
field?.directives?.forEach(dir => {
const connectionAttributeName = getConnectionAttributeName(def.name.value, field.name.value);
let hasFieldsDefined = false;
let removalIndex = -1;
dir?.arguments?.forEach((arg, idx) => {
if (arg.name.value === 'fields') {
if ((arg.value.kind === 'StringValue' && arg.value.value) || (arg.value.kind === 'ListValue' && arg.value.values && arg.value.values.length > 0)) {
hasFieldsDefined = true;
} else {
removalIndex = idx;
}
}
});
if (removalIndex !== -1) {
dir?.arguments?.splice(removalIndex, 1);
}
if (!hasFieldsDefined) {
// eslint-disable-next-line no-param-reassign
dir.arguments = [makeArgument('fields', makeValueNode(connectionAttributeName)) as WritableDraft<ArgumentNode>];
def?.fields?.push(
makeField(
connectionAttributeName, [], isNonNullType(field.type) ?
makeNonNullType(makeNamedType('ID')) : makeNamedType('ID'), [],
) as WritableDraft<FieldDefinitionNode>,
);
}
});
}
});
});
});
return document;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
DirectiveNode,
DocumentNode,
FieldDefinitionNode,
GraphQLError,
InterfaceTypeDefinitionNode,
Kind,
ObjectTypeDefinitionNode,
Expand All @@ -25,7 +24,7 @@ import {
isListType,
makeArgument,
makeDirective,
makeField, makeListType,
makeField,
makeNamedType,
makeValueNode,
toUpper,
Expand Down Expand Up @@ -111,32 +110,30 @@ export class ManyToManyTransformer extends TransformerPluginBase {
mutateSchema = (context: TransformerPreProcessContextProvider): DocumentNode => {
const manyToManyMap = new Map<string, ManyToManyPreProcessContext[]>(); // Relation name is the key, each array should be length 2 (two models being connected)
const newDocument: DocumentNode = produce(context.inputDocument, draftDoc => {
const filteredDefs = draftDoc?.definitions?.filter(def => def.kind === 'ObjectTypeExtension' || def.kind === 'ObjectTypeDefinition');
const objectDefs = filteredDefs as Array<WritableDraft<ObjectTypeDefinitionNode | ObjectTypeExtensionNode>>;
// First iteration builds the map
draftDoc?.definitions?.forEach(def => {
if (def.kind === 'ObjectTypeDefinition' || def.kind === 'ObjectTypeExtension') {
def?.fields?.forEach(field => {
field?.directives?.forEach(dir => {
if (dir.name.value === directiveName) {
const relationArg = dir?.arguments?.find(arg => arg.name.value === 'relationName');
if (relationArg?.value?.kind === 'StringValue') {
const relationName = relationArg.value.value;
const manyToManyContext: ManyToManyPreProcessContext = {
model: def,
field: field,
directive: dir,
modelAuthDirectives: def?.directives?.filter(authDir => authDir.name.value === 'auth') ?? [],
fieldAuthDirectives: def?.directives?.filter(authDir => authDir.name.value === 'auth') ?? [],
relationName: relationName,
};
if (!manyToManyMap.has(relationName)) {
manyToManyMap.set(relationName, []);
}
manyToManyMap.get(relationName)?.push(manyToManyContext);
}
objectDefs?.forEach(def => {
def?.fields?.forEach(field => {
field?.directives?.filter(dir => dir.name.value === directiveName)?.forEach(dir => {
const relationArg = dir?.arguments?.find(arg => arg.name.value === 'relationName');
if (relationArg?.value?.kind === 'StringValue') {
const relationName = relationArg.value.value;
const manyToManyContext: ManyToManyPreProcessContext = {
model: def,
field: field,
directive: dir,
modelAuthDirectives: def?.directives?.filter(authDir => authDir.name.value === 'auth') ?? [],
fieldAuthDirectives: def?.directives?.filter(authDir => authDir.name.value === 'auth') ?? [],
relationName: relationName,
};
if (!manyToManyMap.has(relationName)) {
manyToManyMap.set(relationName, []);
}
});
manyToManyMap.get(relationName)?.push(manyToManyContext);
}
});
}
});
});

// Run check for relations that are not binary and therefore invalid
Expand Down

0 comments on commit 2646fc1

Please sign in to comment.