graphile-build-pg
Version:
Build a GraphQL schema by reflection over a PostgreSQL schema. Easy to customize since it's built with plugins on graphile-build
188 lines (187 loc) • 8.24 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _debug = _interopRequireDefault(require("debug"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const debug = (0, _debug.default)("graphile-build-pg");
var PgMutationCreatePlugin = function PgMutationCreatePlugin(builder, {
pgDisableDefaultMutations
}) {
if (pgDisableDefaultMutations) {
return;
}
builder.hook("GraphQLObjectType:fields", (fields, build, context) => {
const {
extend,
newWithHooks,
parseResolveInfo,
pgIntrospectionResultsByKind,
pgGetGqlTypeByTypeIdAndModifier,
pgGetGqlInputTypeByTypeIdAndModifier,
pgSql: sql,
gql2pg,
graphql: {
GraphQLObjectType,
GraphQLInputObjectType,
GraphQLNonNull,
GraphQLString
},
pgColumnFilter,
inflection,
pgQueryFromResolveData: queryFromResolveData,
pgOmit: omit,
pgViaTemporaryTable: viaTemporaryTable,
describePgEntity,
sqlCommentByAddingTags,
pgField
} = build;
const {
scope: {
isRootMutation
},
fieldWithHooks
} = context;
if (!isRootMutation) {
return fields;
}
return extend(fields, pgIntrospectionResultsByKind.class.reduce((memo, table) => {
// PERFORMANCE: These used to be .filter(...) calls
if (!table.namespace) return memo;
if (!table.isSelectable) return memo;
if (!table.isInsertable || omit(table, "create")) return memo;
const Table = pgGetGqlTypeByTypeIdAndModifier(table.type.id, null);
if (!Table) {
debug(`There was no table type for table '${table.namespace.name}.${table.name}', so we're not generating a create mutation for it.`);
return memo;
}
const TableInput = pgGetGqlInputTypeByTypeIdAndModifier(table.type.id, null);
if (!TableInput) {
debug(`There was no input type for table '${table.namespace.name}.${table.name}', so we're going to omit it from the create mutation.`);
}
const tableTypeName = inflection.tableType(table);
const InputType = newWithHooks(GraphQLInputObjectType, {
name: inflection.createInputType(table),
description: build.wrapDescription(`All input for the create \`${tableTypeName}\` mutation.`, "type"),
fields: {
clientMutationId: {
description: build.wrapDescription("An arbitrary string value with no semantic meaning. Will be included in the payload verbatim. May be used to track mutations by the client.", "field"),
type: GraphQLString
},
...(TableInput ? {
[inflection.tableFieldName(table)]: {
description: build.wrapDescription(`The \`${tableTypeName}\` to be created by this mutation.`, "field"),
type: new GraphQLNonNull(TableInput)
}
} : null)
}
}, {
__origin: `Adding table create input type for ${describePgEntity(table)}. You can rename the table's GraphQL type via a 'Smart Comment':\n\n ${sqlCommentByAddingTags(table, {
name: "newNameHere"
})}`,
isPgCreateInputType: true,
pgInflection: table,
// TODO:v5: remove - TYPO!
pgIntrospection: table
});
const PayloadType = newWithHooks(GraphQLObjectType, {
name: inflection.createPayloadType(table),
description: build.wrapDescription(`The output of our create \`${tableTypeName}\` mutation.`, "type"),
fields: ({
fieldWithHooks
}) => {
const tableName = inflection.tableFieldName(table);
return {
clientMutationId: {
description: build.wrapDescription("The exact same `clientMutationId` that was provided in the mutation input, unchanged and unused. May be used by a client to track mutations.", "field"),
type: GraphQLString
},
[tableName]: pgField(build, fieldWithHooks, tableName, {
description: build.wrapDescription(`The \`${tableTypeName}\` that was created by this mutation.`, "field"),
type: Table
}, {
isPgCreatePayloadResultField: true,
pgFieldIntrospection: table
})
};
}
}, {
__origin: `Adding table create payload type for ${describePgEntity(table)}. You can rename the table's GraphQL type via a 'Smart Comment':\n\n ${sqlCommentByAddingTags(table, {
name: "newNameHere"
})}\n\nor disable the built-in create mutation via:\n\n ${sqlCommentByAddingTags(table, {
omit: "create"
})}`,
isMutationPayload: true,
isPgCreatePayloadType: true,
pgIntrospection: table
});
const fieldName = inflection.createField(table);
memo = build.extend(memo, {
[fieldName]: fieldWithHooks(fieldName, context => {
const {
getDataFromParsedResolveInfoFragment
} = context;
const relevantAttributes = table.attributes.filter(attr => pgColumnFilter(attr, build, context) && !omit(attr, "create"));
return {
description: build.wrapDescription(`Creates a single \`${tableTypeName}\`.`, "field"),
type: PayloadType,
args: {
input: {
type: new GraphQLNonNull(InputType)
}
},
async resolve(data, args, resolveContext, resolveInfo) {
const {
input
} = args;
const {
pgClient
} = resolveContext;
const parsedResolveInfoFragment = parseResolveInfo(resolveInfo);
parsedResolveInfoFragment.args = args; // Allow overriding via makeWrapResolversPlugin
const resolveData = getDataFromParsedResolveInfoFragment(parsedResolveInfoFragment, PayloadType);
const insertedRowAlias = sql.identifier(Symbol());
const query = queryFromResolveData(insertedRowAlias, insertedRowAlias, resolveData, {}, null, resolveContext, resolveInfo.rootValue);
const sqlColumns = [];
const sqlValues = [];
const inputData = input[inflection.tableFieldName(table)];
relevantAttributes.forEach(attr => {
const fieldName = inflection.column(attr);
const val = inputData[fieldName];
if (Object.prototype.hasOwnProperty.call(inputData, fieldName)) {
sqlColumns.push(sql.identifier(attr.name));
sqlValues.push(gql2pg(val, attr.type, attr.typeModifier));
}
});
const mutationQuery = sql.query`\
insert into ${sql.identifier(table.namespace.name, table.name)} ${sqlColumns.length ? sql.fragment`(${sql.join(sqlColumns, ", ")}) values(${sql.join(sqlValues, ", ")})` : sql.fragment`default values`} returning *`;
let row;
try {
await pgClient.query("SAVEPOINT graphql_mutation");
const rows = await viaTemporaryTable(pgClient, sql.identifier(table.namespace.name, table.name), mutationQuery, insertedRowAlias, query);
row = rows[0];
await pgClient.query("RELEASE SAVEPOINT graphql_mutation");
} catch (e) {
await pgClient.query("ROLLBACK TO SAVEPOINT graphql_mutation");
throw e;
}
return {
clientMutationId: input.clientMutationId,
data: row
};
}
};
}, {
pgFieldIntrospection: table,
isPgCreateMutationField: true
})
}, `Adding create mutation for ${describePgEntity(table)}. You can omit this default mutation with a 'Smart Comment':\n\n ${sqlCommentByAddingTags(table, {
omit: "create"
})}`);
return memo;
}, {}), `Adding default 'create' mutation to root mutation`);
}, ["PgMutationCreate"], [], ["PgTables"]);
};
exports.default = PgMutationCreatePlugin;
//# sourceMappingURL=PgMutationCreatePlugin.js.map