@aws-amplify/graphql-schema-generator
Version:
Amplify GraphQL schema generator
255 lines • 16 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.findMatchingField = exports.checkDestructiveNullabilityChange = exports.getParentNode = exports.applyFieldNameOverrides = exports.applyModelAuthOverrides = exports.applyModelNameOverrides = exports.applyJSONFieldTypeOverrides = exports.applyModelOverrides = exports.applyFieldOverrides = exports.applySchemaOverrides = void 0;
const graphql_1 = require("graphql");
const graphql_transformer_common_1 = require("graphql-transformer-common");
const graphql_transformer_core_1 = require("@aws-amplify/graphql-transformer-core");
const MODEL_DIRECTIVE_NAME = 'model';
const REFERS_TO_DIRECTIVE_NAME = 'refersTo';
const RELATIONAL_DIRECTIVES = ['hasOne', 'hasMany', 'belongsTo'];
const AUTH_DIRECTIVE_NAME = 'auth';
const applySchemaOverrides = (document, existingDocument) => {
if (!existingDocument) {
return document;
}
let updatedDocument = preserveRelationalDirectives(document, existingDocument);
const schemaVisitor = {
FieldDefinition: {
leave: (node, key, parent, path, ancestors) => {
const parentObjectType = (0, exports.getParentNode)(ancestors);
if (!parentObjectType || !(parentObjectType === null || parentObjectType === void 0 ? void 0 : parentObjectType.name)) {
return;
}
const columnName = getMappedName(node);
const tableName = getMappedName(parentObjectType);
checkDuplicateFieldMapping(columnName, tableName, existingDocument);
const correspondingField = (0, exports.findMatchingField)(columnName, tableName, existingDocument);
if (!correspondingField)
return;
return (0, exports.applyFieldOverrides)(node, correspondingField);
},
},
ObjectTypeDefinition: {
leave: (node, key, parent, path, ancestors) => {
const tableName = getMappedName(node);
checkDuplicateModelMapping(tableName, existingDocument);
const correspondingModel = findMatchingModel(tableName, existingDocument);
if (!correspondingModel)
return;
return (0, exports.applyModelOverrides)(node, correspondingModel);
},
},
};
updatedDocument = (0, graphql_1.visit)(updatedDocument, schemaVisitor);
updatedDocument['definitions'] = [
...updatedDocument['definitions'],
...(0, graphql_transformer_common_1.getNonModelTypes)(existingDocument),
...getCustomEnumTypes(document, existingDocument),
];
return updatedDocument;
};
exports.applySchemaOverrides = applySchemaOverrides;
const preserveRelationalDirectives = (document, existingDocument) => {
const documentWrapper = document;
existingDocument.definitions
.filter((def) => def.kind === 'ObjectTypeDefinition' && def.directives.find((dir) => dir.name.value === MODEL_DIRECTIVE_NAME))
.forEach((existingObject) => {
const existingTableName = getMappedName(existingObject);
const newObject = findMatchingModel(existingTableName, document);
if (!newObject) {
return;
}
const relationalFields = existingObject.fields.filter((field) => field.directives.find((dir) => RELATIONAL_DIRECTIVES.includes(dir.name.value)));
const newObjectWrapper = new graphql_transformer_core_1.ObjectDefinitionWrapper(newObject);
relationalFields.forEach((relationalField) => {
newObjectWrapper.fields.push(new graphql_transformer_core_1.FieldWrapper(relationalField));
});
if (relationalFields.length > 0) {
const excludedDefinitions = documentWrapper.definitions.filter((def) => getMappedName(def) !== existingTableName);
documentWrapper.definitions = [...excludedDefinitions, newObjectWrapper.serialize()];
}
});
return documentWrapper;
};
const applyFieldOverrides = (field, existingField) => {
return {
...field,
...(0, exports.applyJSONFieldTypeOverrides)(field, existingField),
...(0, exports.applyFieldNameOverrides)(field, existingField),
};
};
exports.applyFieldOverrides = applyFieldOverrides;
const applyModelOverrides = (obj, existingObj) => {
let updatedModel = { ...obj };
updatedModel = (0, exports.applyModelNameOverrides)(obj, existingObj);
updatedModel = (0, exports.applyModelAuthOverrides)(updatedModel, existingObj);
return updatedModel;
};
exports.applyModelOverrides = applyModelOverrides;
const applyJSONFieldTypeOverrides = (field, existingField) => {
const isJSONType = (0, graphql_transformer_common_1.isOfType)(field === null || field === void 0 ? void 0 : field.type, 'AWSJSON');
if (!isJSONType) {
return field;
}
const isExistingFieldArrayOrObject = (0, graphql_transformer_common_1.isArrayOrObject)(existingField === null || existingField === void 0 ? void 0 : existingField.type, []);
if (!isExistingFieldArrayOrObject) {
return field;
}
(0, exports.checkDestructiveNullabilityChange)(field, existingField);
return {
type: existingField === null || existingField === void 0 ? void 0 : existingField.type,
};
};
exports.applyJSONFieldTypeOverrides = applyJSONFieldTypeOverrides;
const applyModelNameOverrides = (obj, existingObj) => {
var _a, _b, _c, _d;
const tableName = getMappedName(obj);
const existingTableName = getMappedName(existingObj);
const existingTypeName = (_a = existingObj === null || existingObj === void 0 ? void 0 : existingObj.name) === null || _a === void 0 ? void 0 : _a.value;
if (tableName !== existingTableName && tableName !== existingTypeName) {
return obj;
}
if (tableName === existingTypeName) {
return {
...obj,
...{ name: existingObj === null || existingObj === void 0 ? void 0 : existingObj.name },
...{ directives: (_b = obj === null || obj === void 0 ? void 0 : obj.directives) === null || _b === void 0 ? void 0 : _b.filter((dir) => dir.name.value !== REFERS_TO_DIRECTIVE_NAME) },
};
}
return {
...obj,
...{ name: existingObj === null || existingObj === void 0 ? void 0 : existingObj.name },
...{
directives: [
...(_c = existingObj === null || existingObj === void 0 ? void 0 : existingObj.directives) === null || _c === void 0 ? void 0 : _c.filter((dir) => dir.name.value === REFERS_TO_DIRECTIVE_NAME),
...(_d = obj === null || obj === void 0 ? void 0 : obj.directives) === null || _d === void 0 ? void 0 : _d.filter((dir) => dir.name.value !== REFERS_TO_DIRECTIVE_NAME),
],
},
};
};
exports.applyModelNameOverrides = applyModelNameOverrides;
const applyModelAuthOverrides = (obj, existingObj) => {
var _a, _b, _c;
const authDirectiveExists = (_a = existingObj === null || existingObj === void 0 ? void 0 : existingObj.directives) === null || _a === void 0 ? void 0 : _a.find((dir) => { var _a; return ((_a = dir === null || dir === void 0 ? void 0 : dir.name) === null || _a === void 0 ? void 0 : _a.value) === AUTH_DIRECTIVE_NAME; });
if (!authDirectiveExists) {
return obj;
}
return {
...obj,
...{
directives: [
...(_b = obj === null || obj === void 0 ? void 0 : obj.directives) === null || _b === void 0 ? void 0 : _b.filter((dir) => dir.name.value !== AUTH_DIRECTIVE_NAME),
...(_c = existingObj === null || existingObj === void 0 ? void 0 : existingObj.directives) === null || _c === void 0 ? void 0 : _c.filter((dir) => dir.name.value === AUTH_DIRECTIVE_NAME),
],
},
};
};
exports.applyModelAuthOverrides = applyModelAuthOverrides;
const applyFieldNameOverrides = (field, existingField) => {
var _a, _b, _c, _d, _e, _f;
const columnName = getMappedName(field);
const existingColumnName = getMappedName(existingField);
const existingFieldName = (_a = existingField === null || existingField === void 0 ? void 0 : existingField.name) === null || _a === void 0 ? void 0 : _a.value;
if (columnName !== existingColumnName && columnName !== existingFieldName) {
return field;
}
const existingFieldIsRelational = (_b = existingField === null || existingField === void 0 ? void 0 : existingField.directives) === null || _b === void 0 ? void 0 : _b.find((dir) => { var _a; return RELATIONAL_DIRECTIVES.includes((_a = dir === null || dir === void 0 ? void 0 : dir.name) === null || _a === void 0 ? void 0 : _a.value); });
const existingFieldHasRefersTo = (_c = existingField === null || existingField === void 0 ? void 0 : existingField.directives) === null || _c === void 0 ? void 0 : _c.find((dir) => { var _a; return ((_a = dir === null || dir === void 0 ? void 0 : dir.name) === null || _a === void 0 ? void 0 : _a.value) === REFERS_TO_DIRECTIVE_NAME; });
if (existingFieldIsRelational && existingFieldHasRefersTo) {
throw new Error(`Field "${existingFieldName}" cannot be renamed because it is a relational field.`);
}
if (columnName === existingFieldName) {
return {
...{ name: existingField === null || existingField === void 0 ? void 0 : existingField.name },
...{ directives: (_d = field === null || field === void 0 ? void 0 : field.directives) === null || _d === void 0 ? void 0 : _d.filter((dir) => dir.name.value !== REFERS_TO_DIRECTIVE_NAME) },
};
}
return {
...{ name: existingField === null || existingField === void 0 ? void 0 : existingField.name },
...{
directives: [
...(_e = existingField === null || existingField === void 0 ? void 0 : existingField.directives) === null || _e === void 0 ? void 0 : _e.filter((dir) => dir.name.value === REFERS_TO_DIRECTIVE_NAME),
...(_f = field === null || field === void 0 ? void 0 : field.directives) === null || _f === void 0 ? void 0 : _f.filter((dir) => dir.name.value !== REFERS_TO_DIRECTIVE_NAME),
],
},
};
};
exports.applyFieldNameOverrides = applyFieldNameOverrides;
const getParentNode = (ancestors) => {
if (ancestors && (ancestors === null || ancestors === void 0 ? void 0 : ancestors.length) > 0) {
return ancestors[ancestors.length - 1];
}
};
exports.getParentNode = getParentNode;
const checkDestructiveNullabilityChange = (field, existingField) => {
var _a;
const isFieldRequired = (0, graphql_transformer_common_1.isNonNullType)(field === null || field === void 0 ? void 0 : field.type);
const isExistingFieldRequired = (0, graphql_transformer_common_1.isNonNullType)(existingField === null || existingField === void 0 ? void 0 : existingField.type);
if (isFieldRequired && !isExistingFieldRequired) {
console.warn(`The field ${(_a = field === null || field === void 0 ? void 0 : field.name) === null || _a === void 0 ? void 0 : _a.value} has been changed to an optional type while it is required in the database. This may result in SQL errors in the mutations.`);
}
};
exports.checkDestructiveNullabilityChange = checkDestructiveNullabilityChange;
const getMappedName = (node) => {
var _a, _b, _c;
const nodeName = (_a = node === null || node === void 0 ? void 0 : node.name) === null || _a === void 0 ? void 0 : _a.value;
const refersToDirective = node.directives.find((dir) => dir.name.value === REFERS_TO_DIRECTIVE_NAME);
if (!refersToDirective) {
return nodeName;
}
const mappedName = (_b = refersToDirective === null || refersToDirective === void 0 ? void 0 : refersToDirective.arguments) === null || _b === void 0 ? void 0 : _b.find((arg) => { var _a; return ((_a = arg === null || arg === void 0 ? void 0 : arg.name) === null || _a === void 0 ? void 0 : _a.value) === 'name'; });
if (!mappedName) {
return nodeName;
}
return (_c = mappedName === null || mappedName === void 0 ? void 0 : mappedName.value) === null || _c === void 0 ? void 0 : _c.value;
};
const findMatchingModel = (tableName, existingDocument) => {
const matchedModel = existingDocument.definitions.find((def) => {
var _a, _b;
return (def === null || def === void 0 ? void 0 : def.kind) === 'ObjectTypeDefinition' &&
((_a = def === null || def === void 0 ? void 0 : def.directives) === null || _a === void 0 ? void 0 : _a.find((dir) => { var _a; return ((_a = dir === null || dir === void 0 ? void 0 : dir.name) === null || _a === void 0 ? void 0 : _a.value) === MODEL_DIRECTIVE_NAME; })) &&
(((_b = def === null || def === void 0 ? void 0 : def.name) === null || _b === void 0 ? void 0 : _b.value) === tableName || getMappedName(def) === tableName);
});
if (matchedModel) {
return matchedModel;
}
};
const findMatchingField = (columnName, taleName, document) => {
var _a;
const matchingObject = findMatchingModel(taleName, document);
if (!matchingObject) {
return;
}
return (_a = matchingObject === null || matchingObject === void 0 ? void 0 : matchingObject.fields) === null || _a === void 0 ? void 0 : _a.find((field) => { var _a; return ((_a = field === null || field === void 0 ? void 0 : field.name) === null || _a === void 0 ? void 0 : _a.value) === columnName || getMappedName(field) === columnName; });
};
exports.findMatchingField = findMatchingField;
const checkDuplicateModelMapping = (tableName, document) => {
const matchedTypes = document.definitions.filter((def) => { var _a; return (def === null || def === void 0 ? void 0 : def.kind) === 'ObjectTypeDefinition' && (((_a = def === null || def === void 0 ? void 0 : def.name) === null || _a === void 0 ? void 0 : _a.value) === tableName || getMappedName(def) === tableName); });
if ((matchedTypes === null || matchedTypes === void 0 ? void 0 : matchedTypes.length) > 1) {
throw new Error(`Types ${matchedTypes
.map((type) => { var _a; return (_a = type === null || type === void 0 ? void 0 : type.name) === null || _a === void 0 ? void 0 : _a.value; })
.join(', ')} are mapped to the same table ${tableName}. Remove the duplicate mapping.`);
}
};
const checkDuplicateFieldMapping = (columnName, tableName, document) => {
var _a;
const matchingObject = findMatchingModel(tableName, document);
if (!matchingObject) {
return;
}
const matchedFields = (_a = matchingObject === null || matchingObject === void 0 ? void 0 : matchingObject.fields) === null || _a === void 0 ? void 0 : _a.filter((def) => { var _a; return ((_a = def === null || def === void 0 ? void 0 : def.name) === null || _a === void 0 ? void 0 : _a.value) === columnName || getMappedName(def) === columnName; });
if ((matchedFields === null || matchedFields === void 0 ? void 0 : matchedFields.length) > 1) {
throw new Error(`Fields ${matchedFields
.map((field) => { var _a; return (_a = field === null || field === void 0 ? void 0 : field.name) === null || _a === void 0 ? void 0 : _a.value; })
.join(', ')} are mapped to the same column ${columnName}. Remove the duplicate mapping.`);
}
};
const getCustomEnumTypes = (document, existingDocument) => {
const existingEnumTypes = getEnumTypes(existingDocument);
const newEnumTypes = getEnumTypes(document);
return existingEnumTypes.filter((existingEnum) => !newEnumTypes.find((newEnum) => { var _a, _b; return ((_a = newEnum === null || newEnum === void 0 ? void 0 : newEnum.name) === null || _a === void 0 ? void 0 : _a.value) === ((_b = existingEnum === null || existingEnum === void 0 ? void 0 : existingEnum.name) === null || _b === void 0 ? void 0 : _b.value); }));
};
const getEnumTypes = (document) => {
return document.definitions.filter((def) => def.kind === 'EnumTypeDefinition').map((def) => def);
};
//# sourceMappingURL=schema-overrides.js.map