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

215 lines (207 loc) 7.34 kB
// @flow import type { Plugin } from "graphile-build"; import isString from "lodash/isString"; export default (function PgMutationPayloadEdgePlugin( builder, { pgSimpleCollections, disableIssue397Fix } ) { builder.hook( "GraphQLObjectType:fields", (fields, build, context) => { const { extend, getSafeAliasFromResolveInfo, getTypeByName, pgGetGqlTypeByTypeIdAndModifier, pgSql: sql, graphql: { GraphQLList, GraphQLNonNull }, inflection, pgOmit: omit, describePgEntity, pgField, } = build; const { scope: { isMutationPayload, pgIntrospection, pgIntrospectionTable }, fieldWithHooks, Self, } = context; const table = pgIntrospectionTable || pgIntrospection; if ( !isMutationPayload || !table || table.kind !== "class" || !table.namespace || !table.isSelectable || (omit(table, "all") && omit(table, "many")) ) { return fields; } if ( pgIntrospection.kind === "procedure" && (pgIntrospection.returnTypeId !== table.typeId || pgIntrospection.returnsSet) ) { return fields; } const simpleCollections = table.tags.simpleCollections || pgSimpleCollections; const hasConnections = simpleCollections !== "only"; if (!hasConnections && !disableIssue397Fix) { return fields; } const TableType = pgGetGqlTypeByTypeIdAndModifier(table.type.id, null); const tableTypeName = TableType.name; const TableOrderByType = getTypeByName( inflection.orderByType(tableTypeName) ); const TableEdgeType = getTypeByName(inflection.edge(tableTypeName)); if (!TableEdgeType) { return fields; } const primaryKeyConstraint = table.primaryKeyConstraint; const primaryKeys = primaryKeyConstraint && primaryKeyConstraint.keyAttributes; const canOrderBy = !omit(table, "order"); const fieldName = inflection.edgeField(table); const primaryKeyAsc = inflection.builtin("PRIMARY_KEY_ASC"); const defaultValueEnum = canOrderBy && (TableOrderByType.getValues().find(v => v.name === primaryKeyAsc) || TableOrderByType.getValues()[0]); return extend( fields, { [fieldName]: pgField( build, fieldWithHooks, fieldName, { description: build.wrapDescription( `An edge for our \`${tableTypeName}\`. May be used by Relay 1.`, "field" ), type: TableEdgeType, args: canOrderBy ? { orderBy: { description: build.wrapDescription( `The method to use when ordering \`${tableTypeName}\`.`, "arg" ), type: new GraphQLList( new GraphQLNonNull(TableOrderByType) ), defaultValue: defaultValueEnum ? [defaultValueEnum.value] : null, }, } : {}, resolve(data, { orderBy: rawOrderBy }, _context, resolveInfo) { if (!data.data) { return null; } const safeAlias = getSafeAliasFromResolveInfo(resolveInfo); const edge = data.data[safeAlias]; if (!edge) { return null; } const orderBy = canOrderBy && rawOrderBy ? Array.isArray(rawOrderBy) ? rawOrderBy : [rawOrderBy] : null; const order = orderBy && orderBy.some(item => item.alias) ? orderBy.filter(item => item.alias) : null; if (!order) { if (edge.__identifiers) { return { ...edge, __cursor: ["primary_key_asc", edge.__identifiers], }; } else { return edge; } } return { ...edge, __cursor: edge[`__order_${order.map(item => item.alias).join("__")}`], }; }, }, { isPgMutationPayloadEdgeField: true, pgFieldIntrospection: table, }, false, { withQueryBuilder(queryBuilder, { parsedResolveInfoFragment }) { const { args: { orderBy: rawOrderBy }, } = parsedResolveInfoFragment; const orderBy = canOrderBy && rawOrderBy ? Array.isArray(rawOrderBy) ? rawOrderBy : [rawOrderBy] : null; if (orderBy != null) { const aliases = []; const expressions = []; let unique = false; orderBy.forEach(item => { const { alias, specs, unique: itemIsUnique } = item; unique = unique || itemIsUnique; const orders = Array.isArray(specs[0]) ? specs : [specs]; orders.forEach(([col, _ascending]) => { if (!col) { return; } const expr = isString(col) ? sql.fragment`${queryBuilder.getTableAlias()}.${sql.identifier( col )}` : typeof col === "function" ? col({ queryBuilder }) : col; expressions.push(expr); }); if (alias == null) return; aliases.push(alias); }); if (!unique && primaryKeys) { // Add PKs primaryKeys.forEach(key => { expressions.push( sql.fragment`${queryBuilder.getTableAlias()}.${sql.identifier( key.name )}` ); }); } if (aliases.length) { queryBuilder.select( sql.fragment`json_build_array(${sql.join( aliases.map(a => sql.fragment`${sql.literal(a)}::text`), ", " )}, json_build_array(${sql.join(expressions, ", ")}))`, "__order_" + aliases.join("__") ); } } }, } ), }, `Adding edge field for table ${describePgEntity( table )} to mutation payload '${Self.name}'` ); }, ["PgMutationPayloadEdge"] ); }: Plugin);