UNPKG

@aws-amplify/graphql-schema-generator

Version:
369 lines 14.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.printSchema = exports.convertToGraphQLFieldName = exports.convertToGraphQLTypeName = exports.getRefersToDirective = exports.isComputeExpression = exports.generateGraphQLSchema = void 0; const graphql_transformer_core_1 = require("@aws-amplify/graphql-transformer-core"); const graphql_1 = require("graphql"); const schema_overrides_1 = require("./schema-overrides"); const pluralize_1 = require("pluralize"); const graphql_transformer_common_1 = require("graphql-transformer-common"); const generateGraphQLSchema = (schema, existingSchemaDocument) => { const models = schema.getModels(); const document = { kind: graphql_1.Kind.DOCUMENT, definitions: [], }; const { includeTables, excludeTables } = getIncludeExcludeConfig(existingSchemaDocument); models.forEach((model) => { if (includeTables.length > 0 && !includeTables.includes(model.getName())) { return; } if (excludeTables.length > 0 && excludeTables.includes(model.getName())) { return; } const primaryKey = model.getPrimaryKey(); if (!primaryKey) { return; } const type = constructObjectType(model); const fields = model.getFields(); const primaryKeyFields = primaryKey === null || primaryKey === void 0 ? void 0 : primaryKey.getFields(); fields.forEach((f) => { if (isEnum(f.type)) { const enumType = constructEnumType(getBaseType(f.type)); if (!document.definitions.find((d) => d.name.value === enumType.name)) { document.definitions.push(enumType.serialize()); } } const field = convertInternalFieldTypeToGraphQL(f, primaryKeyFields.includes(f.name)); type.fields.push(field); }); addPrimaryKey(type, model.getPrimaryKey()); addIndexes(type, model.getIndexes()); document.definitions.push(type.serialize()); }); const documentWithOverrides = (0, schema_overrides_1.applySchemaOverrides)(document, existingSchemaDocument); const schemaStr = (0, exports.printSchema)(documentWithOverrides); return schemaStr; }; exports.generateGraphQLSchema = generateGraphQLSchema; const isEnum = (type) => { if (type.kind === 'NonNull' || type.kind === 'List') { return isEnum(type.type); } return type.kind === 'Enum'; }; const getBaseType = (type) => { if (type.kind === 'NonNull' || type.kind === 'List') { return getBaseType(type.type); } return type; }; const convertInternalFieldTypeToGraphQL = (field, isPrimaryKeyField) => { var _a; const fieldName = field.name; const typeWrappers = []; let fieldType = field.type; while (fieldType.kind !== 'Scalar' && fieldType.kind !== 'Custom' && fieldType.kind !== 'Enum') { typeWrappers.push(fieldType.kind); fieldType = fieldType.type; } const fieldDirectives = []; const fieldTypeName = (0, exports.convertToGraphQLFieldName)(fieldName); const fieldNameNeedsMapping = fieldTypeName !== fieldName; if (fieldNameNeedsMapping) { const fieldNameMappingDirective = (0, exports.getRefersToDirective)(fieldName); fieldDirectives.push(fieldNameMappingDirective); } const fieldHasDefaultValue = (field === null || field === void 0 ? void 0 : field.default) && ((_a = field === null || field === void 0 ? void 0 : field.default) === null || _a === void 0 ? void 0 : _a.value); const fieldIsOptional = fieldHasDefaultValue && !isPrimaryKeyField; if (fieldHasDefaultValue) { const defaultStringValue = String(field.default.value); if (!(0, exports.isComputeExpression)(defaultStringValue)) { fieldDirectives.push(new graphql_transformer_core_1.DirectiveWrapper({ kind: graphql_1.Kind.DIRECTIVE, name: { kind: 'Name', value: 'default', }, arguments: [ { kind: 'Argument', name: { kind: 'Name', value: 'value', }, value: { kind: 'StringValue', value: defaultStringValue, }, }, ], })); } } const result = new graphql_transformer_core_1.FieldWrapper({ kind: 'FieldDefinition', name: { kind: 'Name', value: fieldTypeName, }, type: { kind: 'NamedType', name: { kind: 'Name', value: fieldType.name, }, }, directives: fieldDirectives, }); while (typeWrappers.length > 0) { const wrapperType = typeWrappers.pop(); if (wrapperType === 'List') { result.wrapListType(); } else if (wrapperType === 'NonNull' && !fieldIsOptional) { result.makeNonNullable(); } } return result; }; const constructObjectType = (model) => { const modelName = model.getName(); const directives = []; const modelTypeName = (0, exports.convertToGraphQLTypeName)(modelName); const modelNameNeedsMapping = modelTypeName !== modelName; if (modelNameNeedsMapping) { const modelNameMappingDirective = (0, exports.getRefersToDirective)(modelName); directives.push(modelNameMappingDirective); } const modelDirective = { kind: graphql_1.Kind.DIRECTIVE, name: { kind: 'Name', value: 'model', }, }; directives.push(modelDirective); return new graphql_transformer_core_1.ObjectDefinitionWrapper({ kind: graphql_1.Kind.OBJECT_TYPE_DEFINITION, name: { kind: 'Name', value: modelTypeName, }, fields: [], directives: directives, }); }; const constructEnumType = (type) => { if (!validateEnumValues(type.values)) { throw new Error(`Enum "${type.name}" (values: ${type.values.join(',')}) contains one or more invalid values. Enum values must match the regex [_A-Za-z][_0-9A-Za-z]*.`); } const enumValues = type.values.map((t) => { return { kind: graphql_1.Kind.ENUM_VALUE_DEFINITION, name: { kind: 'Name', value: t, }, }; }); const enumType = new graphql_transformer_core_1.EnumWrapper({ kind: graphql_1.Kind.ENUM_TYPE_DEFINITION, name: { kind: 'Name', value: type.name, }, values: enumValues, }); return enumType; }; const validateEnumValues = (values) => { const regex = new RegExp(/^[_A-Za-z][_0-9A-Za-z]*$/); const containsValidValues = values.every((value) => regex.test(value)); return containsValidValues; }; const addIndexes = (type, indexes) => { indexes.forEach((index) => { const firstField = (0, exports.convertToGraphQLFieldName)(index.getFields()[0]); const indexField = type.getField(firstField); const indexArguments = []; indexArguments.push({ kind: 'Argument', name: { kind: 'Name', value: 'name', }, value: { kind: 'StringValue', value: index.name, }, }); if (index.getFields().length > 1) { indexArguments.push({ kind: 'Argument', name: { kind: 'Name', value: 'sortKeyFields', }, value: { kind: 'ListValue', values: index .getFields() .slice(1) .map((k) => { return { kind: 'StringValue', value: (0, exports.convertToGraphQLFieldName)(k), }; }), }, }); } indexField.directives.push(new graphql_transformer_core_1.DirectiveWrapper({ kind: graphql_1.Kind.DIRECTIVE, name: { kind: 'Name', value: 'index', }, arguments: indexArguments, })); }); }; const addPrimaryKey = (type, primaryKey) => { if (!primaryKey) { return; } const firstField = (0, exports.convertToGraphQLFieldName)(primaryKey.getFields()[0]); const primaryKeyField = type.getField(firstField); const keyArguments = []; if (primaryKey.getFields().length > 1) { keyArguments.push({ kind: 'Argument', name: { kind: 'Name', value: 'sortKeyFields', }, value: { kind: 'ListValue', values: primaryKey .getFields() .slice(1) .map((k) => { return { kind: 'StringValue', value: (0, exports.convertToGraphQLFieldName)(k), }; }), }, }); } primaryKeyField.directives.push(new graphql_transformer_core_1.DirectiveWrapper({ kind: graphql_1.Kind.DIRECTIVE, name: { kind: 'Name', value: 'primaryKey', }, arguments: keyArguments, })); }; const getIncludeExcludeConfig = (document) => { var _a, _b; const emptyConfig = { includeTables: [], excludeTables: [] }; if (!document) { return emptyConfig; } const amplifyInputType = document.definitions.find((d) => d.kind === 'InputObjectTypeDefinition' && d.name.value === 'AMPLIFY'); if (!amplifyInputType) { return emptyConfig; } const includeFieldNodeValue = (_a = amplifyInputType.fields.find((f) => f.name.value === 'include')) === null || _a === void 0 ? void 0 : _a.defaultValue; const excludeFieldNodeValue = (_b = amplifyInputType.fields.find((f) => f.name.value === 'exclude')) === null || _b === void 0 ? void 0 : _b.defaultValue; if (includeFieldNodeValue && includeFieldNodeValue.kind !== 'ListValue') { throw new Error('Invalid value for include option. Please check your GraphQL schema.'); } if (excludeFieldNodeValue && excludeFieldNodeValue.kind !== 'ListValue') { throw new Error('Invalid value for include option. Please check your GraphQL schema.'); } const includeTables = includeFieldNodeValue === null || includeFieldNodeValue === void 0 ? void 0 : includeFieldNodeValue.values.map((v) => v.value); const excludeTables = excludeFieldNodeValue === null || excludeFieldNodeValue === void 0 ? void 0 : excludeFieldNodeValue.values.map((v) => v.value); if (includeTables && includeTables.length > 0 && excludeTables && excludeTables.length > 0) { throw new Error('Cannot specify both include and exclude options. Please check your GraphQL schema.'); } return { includeTables: includeTables || [], excludeTables: excludeTables || [], }; }; const isComputeExpression = (value) => { const isSimpleComputedExpression = value.match(/^[a-zA-Z0-9]+\(.*\)/); const isComplexComputedExpression = value.match(/^\([a-zA-Z0-9]+\(.*\)\)/); if (isSimpleComputedExpression || isComplexComputedExpression) { return true; } return false; }; exports.isComputeExpression = isComputeExpression; const getRefersToDirective = (name) => { return { kind: graphql_1.Kind.DIRECTIVE, name: { kind: 'Name', value: 'refersTo', }, arguments: [ { kind: 'Argument', name: { kind: 'Name', value: 'name', }, value: { kind: 'StringValue', value: name, }, }, ], }; }; exports.getRefersToDirective = getRefersToDirective; const convertToGraphQLTypeName = (modelName) => { const cleanedInput = cleanMappedName(modelName); return (0, pluralize_1.singular)((0, graphql_transformer_common_1.toPascalCase)(cleanedInput === null || cleanedInput === void 0 ? void 0 : cleanedInput.split('_'))); }; exports.convertToGraphQLTypeName = convertToGraphQLTypeName; const convertToGraphQLFieldName = (fieldName) => { const cleanedInput = cleanMappedName(fieldName, true); return (0, graphql_transformer_common_1.toCamelCase)(cleanedInput === null || cleanedInput === void 0 ? void 0 : cleanedInput.split('_')); }; exports.convertToGraphQLFieldName = convertToGraphQLFieldName; const cleanMappedName = (name, isField = false) => { if (!(name === null || name === void 0 ? void 0 : name.match(/[a-zA-Z]/))) { const suffix = name === null || name === void 0 ? void 0 : name.replace(/[^0-9]+/g, ''); return isField ? `field${suffix}` : `Model${suffix}`; } const cleanedInput = name .replace(/^[^a-zA-Z]+/, '') .replace(/[^a-zA-Z0-9_]+/g, '_') .trim(); return cleanedInput; }; const printSchema = (document) => { const sortedDocument = sortDocument(document); return (0, graphql_1.print)(sortedDocument); }; exports.printSchema = printSchema; const sortDocument = (document) => { const documentWrapper = document; documentWrapper.definitions = [...document === null || document === void 0 ? void 0 : document.definitions].sort((def1, def2) => { var _a, _b, _c, _d; if (((_a = def1 === null || def1 === void 0 ? void 0 : def1.name) === null || _a === void 0 ? void 0 : _a.value) > ((_b = def2 === null || def2 === void 0 ? void 0 : def2.name) === null || _b === void 0 ? void 0 : _b.value)) { return 1; } if (((_c = def1 === null || def1 === void 0 ? void 0 : def1.name) === null || _c === void 0 ? void 0 : _c.value) < ((_d = def2 === null || def2 === void 0 ? void 0 : def2.name) === null || _d === void 0 ? void 0 : _d.value)) { return -1; } return 0; }); return documentWrapper; }; //# sourceMappingURL=generate-schema.js.map