UNPKG

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

189 lines (188 loc) 7.52 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _debugSql = _interopRequireDefault(require("./debugSql")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var PgRowNode = async function PgRowNode(builder, { subscriptions }) { builder.hook("GraphQLObjectType", (object, build, context) => { const { addNodeFetcherForTypeName, pgSql: sql, gql2pg, pgQueryFromResolveData: queryFromResolveData, pgOmit: omit, pgPrepareAndRun } = build; const { scope: { isPgRowType, pgIntrospection: table } } = context; if (!addNodeFetcherForTypeName) { // Node plugin must be disabled. return object; } if (!isPgRowType || !table.namespace || omit(table, "read")) { return object; } const sqlFullTableName = sql.identifier(table.namespace.name, table.name); const primaryKeyConstraint = table.primaryKeyConstraint; if (!primaryKeyConstraint) { return object; } const primaryKeys = primaryKeyConstraint && primaryKeyConstraint.keyAttributes; addNodeFetcherForTypeName(object.name, async (data, identifiers, resolveContext, parsedResolveInfoFragment, ReturnType, resolveData, resolveInfo) => { const { pgClient } = resolveContext; const liveRecord = resolveInfo && resolveInfo.rootValue && resolveInfo.rootValue.liveRecord; if (identifiers.length !== primaryKeys.length) { throw new Error("Invalid ID"); } const query = queryFromResolveData(sqlFullTableName, undefined, resolveData, { useAsterisk: false // Because it's only a single relation, no need }, queryBuilder => { if (subscriptions && table.primaryKeyConstraint) { queryBuilder.selectIdentifiers(table); } primaryKeys.forEach((key, idx) => { queryBuilder.where(sql.fragment`${queryBuilder.getTableAlias()}.${sql.identifier(key.name)} = ${gql2pg(identifiers[idx], primaryKeys[idx].type, primaryKeys[idx].typeModifier)}`); }); }, resolveContext, resolveInfo && resolveInfo.rootValue); const { text, values } = sql.compile(query); if (_debugSql.default.enabled) (0, _debugSql.default)(text); const { rows: [row] } = await pgPrepareAndRun(pgClient, text, values); if (subscriptions && liveRecord && row) { liveRecord("pg", table, row.__identifiers); } return row; }); return object; }, ["PgRowNode"]); builder.hook("GraphQLObjectType:fields", (fields, build, context) => { const { nodeIdFieldName, getTypeAndIdentifiersFromNodeId, extend, parseResolveInfo, pgGetGqlTypeByTypeIdAndModifier, pgIntrospectionResultsByKind: introspectionResultsByKind, pgSql: sql, gql2pg, graphql: { GraphQLNonNull, GraphQLID }, inflection, pgQueryFromResolveData: queryFromResolveData, pgOmit: omit, describePgEntity, sqlCommentByAddingTags, pgPrepareAndRun } = build; const { scope: { isRootQuery }, fieldWithHooks } = context; if (!isRootQuery || !nodeIdFieldName) { return fields; } return extend(fields, introspectionResultsByKind.class.reduce((memo, table) => { // PERFORMANCE: These used to be .filter(...) calls if (!table.namespace) return memo; if (omit(table, "read")) return memo; const TableType = pgGetGqlTypeByTypeIdAndModifier(table.type.id, null); const sqlFullTableName = sql.identifier(table.namespace.name, table.name); if (TableType) { const primaryKeyConstraint = table.primaryKeyConstraint; if (!primaryKeyConstraint) { return memo; } const primaryKeys = primaryKeyConstraint && primaryKeyConstraint.keyAttributes; const fieldName = inflection.tableNode(table); memo = extend(memo, { [fieldName]: fieldWithHooks(fieldName, ({ getDataFromParsedResolveInfoFragment }) => { return { description: build.wrapDescription(`Reads a single \`${TableType.name}\` using its globally unique \`ID\`.`, "field"), type: TableType, args: { [nodeIdFieldName]: { description: build.wrapDescription(`The globally unique \`ID\` to be used in selecting a single \`${TableType.name}\`.`, "arg"), type: new GraphQLNonNull(GraphQLID) } }, async resolve(parent, args, resolveContext, resolveInfo) { const { pgClient } = resolveContext; const liveRecord = resolveInfo.rootValue && resolveInfo.rootValue.liveRecord; const nodeId = args[nodeIdFieldName]; try { const { Type, identifiers } = getTypeAndIdentifiersFromNodeId(nodeId); if (Type !== TableType) { throw new Error("Mismatched type"); } if (identifiers.length !== primaryKeys.length) { throw new Error("Invalid ID"); } const parsedResolveInfoFragment = parseResolveInfo(resolveInfo); parsedResolveInfoFragment.args = args; // Allow overriding via makeWrapResolversPlugin const resolveData = getDataFromParsedResolveInfoFragment(parsedResolveInfoFragment, TableType); const query = queryFromResolveData(sqlFullTableName, undefined, resolveData, { useAsterisk: false // Because it's only a single relation, no need }, queryBuilder => { if (subscriptions && table.primaryKeyConstraint) { queryBuilder.selectIdentifiers(table); } primaryKeys.forEach((key, idx) => { queryBuilder.where(sql.fragment`${queryBuilder.getTableAlias()}.${sql.identifier(key.name)} = ${gql2pg(identifiers[idx], primaryKeys[idx].type, primaryKeys[idx].typeModifier)}`); }); }, resolveContext, resolveInfo.rootValue); const { text, values } = sql.compile(query); if (_debugSql.default.enabled) (0, _debugSql.default)(text); const { rows: [row] } = await pgPrepareAndRun(pgClient, text, values); if (liveRecord && row) { liveRecord("pg", table, row.__identifiers); } return row; } catch (e) { return null; } } }; }, { isPgNodeQuery: true, pgFieldIntrospection: table }) }, `Adding row by globally unique identifier field for ${describePgEntity(table)}. You can rename this table via a 'Smart Comment':\n\n ${sqlCommentByAddingTags(table, { name: "newNameHere" })}`); } return memo; }, {}), `Adding "row by node ID" fields to root Query type`); }, ["PgRowNode"]); }; exports.default = PgRowNode; //# sourceMappingURL=PgRowNode.js.map