@aws-amplify/graphql-transformer-core
Version:
A framework to transform from GraphQL SDL to AWS CloudFormation.
507 lines • 19.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TransformerOutput = exports.objectExtension = exports.blankObject = void 0;
const graphql_1 = require("graphql");
const strip_directives_1 = require("../utils/strip-directives");
const defaultSchema_1 = require("../utils/defaultSchema");
const AMPLIFY = 'AMPLIFY';
function blankObject(name) {
return {
kind: 'ObjectTypeDefinition',
name: {
kind: 'Name',
value: name,
},
fields: [],
directives: [],
interfaces: [],
};
}
exports.blankObject = blankObject;
function objectExtension(name, fields = []) {
return {
kind: graphql_1.Kind.OBJECT_TYPE_EXTENSION,
name: {
kind: 'Name',
value: name,
},
fields,
directives: [],
interfaces: [],
};
}
exports.objectExtension = objectExtension;
class TransformerOutput {
constructor(inputDocument) {
this.nodeMap = {};
const extensionNodes = [];
for (const inputDef of inputDocument.definitions) {
switch (inputDef.kind) {
case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
case graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION:
case graphql_1.Kind.ENUM_TYPE_DEFINITION:
case graphql_1.Kind.UNION_TYPE_DEFINITION: {
const typeDef = inputDef;
if (this.isAmplifyInput(typeDef.name.value))
break;
if (!this.getType(typeDef.name.value)) {
this.addType(typeDef);
}
break;
}
case graphql_1.Kind.SCHEMA_DEFINITION:
if (!this.getSchema()) {
const typeDef = inputDef;
this.putSchema(typeDef);
}
break;
case graphql_1.Kind.OBJECT_TYPE_EXTENSION:
case graphql_1.Kind.ENUM_TYPE_EXTENSION:
case graphql_1.Kind.UNION_TYPE_EXTENSION:
case graphql_1.Kind.INTERFACE_TYPE_EXTENSION:
case graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION:
extensionNodes.push(inputDef);
break;
default:
}
}
for (const ext of extensionNodes) {
switch (ext.kind) {
case graphql_1.Kind.OBJECT_TYPE_EXTENSION:
this.addObjectExtension(ext);
break;
case graphql_1.Kind.INTERFACE_TYPE_EXTENSION:
this.addInterfaceExtension(ext);
break;
case graphql_1.Kind.UNION_TYPE_EXTENSION:
this.addUnionExtension(ext);
break;
case graphql_1.Kind.ENUM_TYPE_EXTENSION:
this.addEnumExtension(ext);
break;
case graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION:
this.addInputExtension(ext);
break;
default:
continue;
}
}
if (!this.getSchema()) {
this.putSchema(defaultSchema_1.DEFAULT_SCHEMA_DEFINITION);
}
}
getTypeDefinitionsOfKind(kind) {
const typeDefs = [];
for (const key of Object.keys(this.nodeMap)) {
const definition = this.nodeMap[key];
if (definition.kind === kind) {
typeDefs.push(definition);
}
}
return typeDefs;
}
putSchema(obj) {
this.nodeMap.__schema = obj;
}
getSchema() {
return this.nodeMap.__schema;
}
getQueryTypeName() {
const schemaNode = this.getSchema();
const queryTypeName = schemaNode.operationTypes.find((op) => op.operation === 'query');
if (queryTypeName && queryTypeName.type && queryTypeName.type.name) {
return queryTypeName.type.name.value;
}
}
getQuery() {
const queryTypeName = this.getQueryTypeName();
if (queryTypeName) {
return this.nodeMap[queryTypeName];
}
}
addDefaultQuery() {
this.addOperationType(defaultSchema_1.DEFAULT_QUERY_OPERATION);
}
ensureQuery() {
if (!this.getQueryTypeName()) {
this.addDefaultQuery();
}
}
getMutationTypeName() {
const schemaNode = this.getSchema();
const mutationTypeName = schemaNode.operationTypes.find((op) => op.operation === 'mutation');
if (mutationTypeName && mutationTypeName.type && mutationTypeName.type.name) {
return mutationTypeName.type.name.value;
}
}
getMutation() {
const mutationTypeName = this.getMutationTypeName();
if (mutationTypeName) {
return this.nodeMap[mutationTypeName];
}
}
addDefaultMutation() {
this.addOperationType(defaultSchema_1.DEFAULT_MUTATION_OPERATION);
}
ensureMutation() {
if (!this.getMutationTypeName()) {
this.addDefaultMutation();
}
}
getSubscriptionTypeName() {
const schemaNode = this.getSchema();
const subscriptionTypeName = schemaNode.operationTypes.find((op) => op.operation === 'subscription');
if (subscriptionTypeName && subscriptionTypeName.type && subscriptionTypeName.type.name) {
return subscriptionTypeName.type.name.value;
}
}
getSubscription() {
const subscriptionTypeName = this.getSubscriptionTypeName();
if (subscriptionTypeName) {
return this.nodeMap[subscriptionTypeName];
}
}
addDefaultSubscription() {
this.addOperationType(defaultSchema_1.DEFAULT_SUBSCRIPTION_OPERATION);
}
ensureSubscription() {
if (!this.getSubscriptionTypeName()) {
this.addDefaultSubscription();
}
}
addType(obj) {
if (this.nodeMap[obj.name.value]) {
throw new Error(`Conflicting type '${obj.name.value}' found.`);
}
this.nodeMap[obj.name.value] = obj;
}
putType(obj) {
this.nodeMap[obj.name.value] = obj;
}
getType(name) {
return this.nodeMap[name];
}
hasType(name) {
return name in this.nodeMap;
}
addOperationType(operation) {
const schemaNode = this.getSchema();
if (schemaNode.operationTypes.find((op) => op.operation === operation.operation)) {
throw new Error(`Conflicting ${operation.operation} operation found.`);
}
else {
const updatedSchema = TransformerOutput.makeSchema([...schemaNode.operationTypes, operation]);
this.putSchema(updatedSchema);
}
}
addObject(obj) {
if (this.nodeMap[obj.name.value]) {
throw new Error(`Conflicting type '${obj.name.value}' found.`);
}
this.nodeMap[obj.name.value] = obj;
}
updateObject(obj) {
if (!this.nodeMap[obj.name.value]) {
throw new Error(`Type ${obj.name.value} does not exist.`);
}
this.nodeMap[obj.name.value] = obj;
}
getObject(name) {
if (this.nodeMap[name]) {
const node = this.nodeMap[name];
if (node.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
return node;
}
}
}
addUnion(obj) {
if (this.nodeMap[obj.name.value]) {
throw new Error(`Conflicting union '${obj.name.value}' found.`);
}
this.nodeMap[obj.name.value] = obj;
}
addQueryFields(fields) {
this.ensureQuery();
const queryTypeName = this.getQueryTypeName();
if (queryTypeName) {
if (!this.getType(queryTypeName)) {
this.addType(blankObject(queryTypeName));
}
let queryType = objectExtension(queryTypeName, fields);
this.addObjectExtension(queryType);
}
}
addMutationFields(fields) {
this.ensureMutation();
const mutationTypeName = this.getMutationTypeName();
if (mutationTypeName) {
if (!this.getType(mutationTypeName)) {
this.addType(blankObject(mutationTypeName));
}
let mutationType = objectExtension(mutationTypeName, fields);
this.addObjectExtension(mutationType);
}
}
addSubscriptionFields(fields) {
this.ensureSubscription();
const subscriptionTypeName = this.getSubscriptionTypeName();
if (subscriptionTypeName) {
if (!this.getType(subscriptionTypeName)) {
this.addType(blankObject(subscriptionTypeName));
}
let subscriptionType = objectExtension(subscriptionTypeName, fields);
this.addObjectExtension(subscriptionType);
}
}
addObjectExtension(obj) {
if (!this.nodeMap[obj.name.value]) {
throw new Error(`Cannot extend nonexistent type '${obj.name.value}'.`);
}
const oldNode = this.getObject(obj.name.value);
if (!oldNode) {
throw new Error('Failure adding object extension');
}
const newDirs = [];
const oldDirs = (oldNode === null || oldNode === void 0 ? void 0 : oldNode.directives) || [];
if (obj.directives) {
for (const newDir of obj.directives) {
if (Boolean(oldDirs.find((d) => d.name.value === newDir.name.value)) === false) {
newDirs.push(newDir);
}
}
}
const mergedDirs = [...oldDirs, ...newDirs];
const oldFields = (oldNode === null || oldNode === void 0 ? void 0 : oldNode.fields) || [];
const oldFieldMap = oldFields.reduce((acc, field) => ({
...acc,
[field.name.value]: field,
}), {});
const newFields = obj.fields || [];
const mergedFields = [...oldFields];
for (const newField of newFields) {
if (oldFieldMap[newField.name.value]) {
throw new Error(`Object type extension '${obj.name.value}' cannot redeclare field ${newField.name.value}`);
}
mergedFields.push(newField);
}
const oldInterfaces = (oldNode === null || oldNode === void 0 ? void 0 : oldNode.interfaces) || [];
const oldInterfaceMap = oldInterfaces.reduce((acc, field) => ({
...acc,
[field.name.value]: field,
}), {});
const newInterfaces = obj.interfaces || [];
const mergedInterfaces = [...oldInterfaces];
for (const newInterface of newInterfaces) {
if (oldInterfaceMap[newInterface.name.value]) {
throw new Error(`Object type extension '${obj.name.value}' cannot redeclare interface ${newInterface.name.value}`);
}
mergedInterfaces.push(newInterface);
}
this.nodeMap[oldNode.name.value] = {
...oldNode,
interfaces: mergedInterfaces,
directives: mergedDirs,
fields: mergedFields,
};
}
addInputExtension(obj) {
if (!this.nodeMap[obj.name.value]) {
throw new Error(`Cannot extend nonexistent input '${obj.name.value}'.`);
}
const oldNode = this.getType(obj.name.value);
const newDirs = obj.directives || [];
const oldDirs = oldNode.directives || [];
const mergedDirs = [...oldDirs, ...newDirs];
const oldFields = oldNode.fields || [];
const oldFieldMap = oldFields.reduce((acc, field) => ({
...acc,
[field.name.value]: field,
}), {});
const newFields = obj.fields || [];
const mergedFields = [...oldFields];
for (const newField of newFields) {
if (oldFieldMap[newField.name.value]) {
throw new Error(`Input object type extension '${obj.name.value}' cannot redeclare field ${newField.name.value}`);
}
mergedFields.push(newField);
}
this.nodeMap[oldNode.name.value] = {
...oldNode,
directives: mergedDirs,
fields: mergedFields,
};
}
addInterfaceExtension(obj) {
if (!this.nodeMap[obj.name.value]) {
throw new Error(`Cannot extend nonexistent interface '${obj.name.value}'.`);
}
const oldNode = this.getType(obj.name.value);
const newDirs = obj.directives || [];
const oldDirs = oldNode.directives || [];
const mergedDirs = [...oldDirs, ...newDirs];
const oldFields = oldNode.fields || [];
const oldFieldMap = oldFields.reduce((acc, field) => ({
...acc,
[field.name.value]: field,
}), {});
const newFields = obj.fields || [];
const mergedFields = [...oldFields];
for (const newField of newFields) {
if (oldFieldMap[newField.name.value]) {
throw new Error(`Interface type extension '${obj.name.value}' cannot redeclare field ${newField.name.value}`);
}
mergedFields.push(newField);
}
this.nodeMap[oldNode.name.value] = {
...oldNode,
directives: mergedDirs,
fields: mergedFields,
};
}
addUnionExtension(obj) {
if (!this.nodeMap[obj.name.value]) {
throw new Error(`Cannot extend nonexistent union '${obj.name.value}'.`);
}
const oldNode = this.getType(obj.name.value);
const newDirs = obj.directives || [];
const oldDirs = oldNode.directives || [];
const mergedDirs = [...oldDirs, ...newDirs];
const oldTypes = oldNode.types || [];
const oldTypeMap = oldTypes.reduce((acc, type) => ({
...acc,
[type.name.value]: true,
}), {});
const newTypes = obj.types || [];
const mergedFields = [...oldTypes];
for (const newType of newTypes) {
if (oldTypeMap[newType.name.value]) {
throw new Error(`Union type extension '${obj.name.value}' cannot redeclare type ${newType.name.value}`);
}
mergedFields.push(newType);
}
this.nodeMap[oldNode.name.value] = {
...oldNode,
directives: mergedDirs,
types: mergedFields,
};
}
addEnumExtension(obj) {
if (!this.nodeMap[obj.name.value]) {
throw new Error(`Cannot extend nonexistent enum '${obj.name.value}'.`);
}
const oldNode = this.getType(obj.name.value);
const newDirs = obj.directives || [];
const oldDirs = oldNode.directives || [];
const mergedDirs = [...oldDirs, ...newDirs];
const oldValues = oldNode.values || [];
const oldValuesMap = oldValues.reduce((acc, type) => ({
...acc,
[type.name.value]: true,
}), {});
const newValues = obj.values || [];
const mergedValues = [...oldValues];
for (const newValue of newValues) {
if (oldValuesMap[newValue.name.value]) {
throw new Error(`Enum type extension '${obj.name.value}' cannot redeclare value ${newValue.name.value}`);
}
mergedValues.push(newValue);
}
this.nodeMap[oldNode.name.value] = {
...oldNode,
directives: mergedDirs,
values: mergedValues,
};
}
addInput(inp) {
if (this.nodeMap[inp.name.value]) {
throw new Error(`Conflicting input type '${inp.name.value}' found.`);
}
this.nodeMap[inp.name.value] = inp;
}
updateInput(obj) {
if (!this.nodeMap[obj.name.value]) {
throw new Error(`Type ${obj.name.value} does not exist.`);
}
this.nodeMap[obj.name.value] = obj;
}
getInput(name) {
if (this.nodeMap[name]) {
const node = this.nodeMap[name];
if (node.kind === graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION) {
return node;
}
}
}
addEnum(en) {
if (this.nodeMap[en.name.value]) {
throw new Error(`Conflicting enum type '${en.name.value}' found.`);
}
this.nodeMap[en.name.value] = en;
}
buildSchema() {
var _a, _b, _c;
const mutationNode = this.getMutation();
const queryNode = this.getQuery();
const subscriptionNode = this.getSubscription();
let includeMutation = true;
let includeQuery = true;
let includeSubscription = true;
if (!mutationNode || ((_a = mutationNode === null || mutationNode === void 0 ? void 0 : mutationNode.fields) === null || _a === void 0 ? void 0 : _a.length) === 0) {
delete this.nodeMap.Mutation;
includeMutation = false;
}
if (!queryNode || ((_b = queryNode === null || queryNode === void 0 ? void 0 : queryNode.fields) === null || _b === void 0 ? void 0 : _b.length) === 0) {
delete this.nodeMap.Query;
includeQuery = false;
}
if (!subscriptionNode || ((_c = subscriptionNode === null || subscriptionNode === void 0 ? void 0 : subscriptionNode.fields) === null || _c === void 0 ? void 0 : _c.length) === 0) {
delete this.nodeMap.Subscription;
includeSubscription = false;
}
const ops = [];
if (includeQuery) {
ops.push(TransformerOutput.makeOperationType('query', queryNode.name.value));
}
if (includeMutation) {
ops.push(TransformerOutput.makeOperationType('mutation', mutationNode.name.value));
}
if (includeSubscription) {
ops.push(TransformerOutput.makeOperationType('subscription', subscriptionNode.name.value));
}
const schema = TransformerOutput.makeSchema(ops);
this.putSchema(schema);
const astSansDirectives = (0, strip_directives_1.stripDirectives)({
kind: 'Document',
definitions: Object.values(this.nodeMap),
}, ['aws_subscribe', 'aws_auth', 'aws_api_key', 'aws_iam', 'aws_oidc', 'aws_cognito_user_pools', 'aws_lambda', 'deprecated']);
const SDL = (0, graphql_1.print)(astSansDirectives);
return SDL;
}
static makeOperationType(operation, type) {
return {
kind: 'OperationTypeDefinition',
operation,
type: {
kind: 'NamedType',
name: {
kind: 'Name',
value: type,
},
},
};
}
static makeSchema(operationTypes) {
return {
kind: graphql_1.Kind.SCHEMA_DEFINITION,
operationTypes,
directives: [],
};
}
isAmplifyInput(inputName) {
return inputName === AMPLIFY;
}
}
exports.TransformerOutput = TransformerOutput;
//# sourceMappingURL=output.js.map