@graphile-contrib/pg-many-to-many
Advanced tools
Comparing version 1.0.0-beta.10 to 1.0.0-beta.11
11
index.js
@@ -8,7 +8,3 @@ module.exports = function PgManyToManyPlugin(builder, options) { | ||
throw new Error( | ||
`Plugin ${pkg.name}@${ | ||
pkg.version | ||
} requires graphile-build@^4.1.0 in order to check dependencies (current version: ${ | ||
build.graphileBuildVersion | ||
})` | ||
`Plugin ${pkg.name}@${pkg.version} requires graphile-build@^4.1.0 in order to check dependencies (current version: ${build.graphileBuildVersion})` | ||
); | ||
@@ -27,3 +23,3 @@ } | ||
}; | ||
depends("graphile-build-pg", "^4.3.1"); | ||
depends("graphile-build-pg", "^4.4.0"); | ||
@@ -36,3 +32,6 @@ // Register this plugin | ||
require("./src/PgManyToManyRelationInflectionPlugin.js")(builder, options); | ||
require("./src/PgManyToManyRelationPlugin.js")(builder, options); | ||
require("./src/PgManyToManyRelationEdgeColumnsPlugin.js")(builder, options); | ||
require("./src/PgManyToManyRelationEdgeTablePlugin.js")(builder, options); | ||
}; |
{ | ||
"name": "@graphile-contrib/pg-many-to-many", | ||
"version": "1.0.0-beta.10", | ||
"version": "1.0.0-beta.11", | ||
"description": "Add connection fields for many-to-many relations", | ||
@@ -20,11 +20,11 @@ "main": "index.js", | ||
"devDependencies": { | ||
"eslint": "^5.16.0", | ||
"eslint-config-prettier": "^4.0.0", | ||
"eslint-plugin-jest": "^22.2.2", | ||
"eslint-plugin-prettier": "^3.0.1", | ||
"graphql": "^14.0.2", | ||
"jest": "^24.8.0", | ||
"pg": "^7.8.0", | ||
"postgraphile-core": "4.3.1", | ||
"prettier": "1.16.4" | ||
"eslint": "^6.4.0", | ||
"eslint-config-prettier": "^6.3.0", | ||
"eslint-plugin-jest": "^22.17.0", | ||
"eslint-plugin-prettier": "^3.1.1", | ||
"graphql": "^14.5.7", | ||
"jest": "^24.9.0", | ||
"pg": "^7.12.1", | ||
"postgraphile-core": "4.4.0", | ||
"prettier": "1.18.2" | ||
}, | ||
@@ -31,0 +31,0 @@ "jest": { |
@@ -7,3 +7,3 @@ # @graphile-contrib/pg-many-to-many | ||
> Requires `postgraphile@^4.3.1` or `graphile-build-pg@^4.3.1` | ||
> Requires `postgraphile@^4.4.0` or `graphile-build-pg@^4.4.0` | ||
@@ -10,0 +10,0 @@ Example: |
@@ -1,64 +0,10 @@ | ||
module.exports = function PgManyToManyRelationPlugin( | ||
builder, | ||
{ pgSimpleCollections } | ||
) { | ||
builder.hook("inflection", inflection => { | ||
return Object.assign(inflection, { | ||
manyToManyRelationByKeys( | ||
_leftKeyAttributes, | ||
junctionLeftKeyAttributes, | ||
junctionRightKeyAttributes, | ||
_rightKeyAttributes, | ||
junctionTable, | ||
rightTable, | ||
_junctionLeftConstraint, | ||
junctionRightConstraint | ||
) { | ||
if (junctionRightConstraint.tags.manyToManyFieldName) { | ||
return junctionRightConstraint.tags.manyToManyFieldName; | ||
} | ||
return this.camelCase( | ||
`${this.pluralize( | ||
this._singularizedTableName(rightTable) | ||
)}-by-${this._singularizedTableName(junctionTable)}-${[ | ||
...junctionLeftKeyAttributes, | ||
...junctionRightKeyAttributes, | ||
] | ||
.map(attr => this.column(attr)) | ||
.join("-and-")}` | ||
); | ||
}, | ||
manyToManyRelationByKeysSimple( | ||
_leftKeyAttributes, | ||
junctionLeftKeyAttributes, | ||
junctionRightKeyAttributes, | ||
_rightKeyAttributes, | ||
junctionTable, | ||
rightTable, | ||
_junctionLeftConstraint, | ||
junctionRightConstraint | ||
) { | ||
if (junctionRightConstraint.tags.manyToManySimpleFieldName) { | ||
return junctionRightConstraint.tags.manyToManySimpleFieldName; | ||
} | ||
return this.camelCase( | ||
`${this.pluralize( | ||
this._singularizedTableName(rightTable) | ||
)}-by-${this._singularizedTableName(junctionTable)}-${[ | ||
...junctionLeftKeyAttributes, | ||
...junctionRightKeyAttributes, | ||
] | ||
.map(attr => this.column(attr)) | ||
.join("-and-")}-list` | ||
); | ||
}, | ||
}); | ||
}); | ||
const createManyToManyConnectionType = require("./createManyToManyConnectionType"); | ||
const manyToManyRelationships = require("./manyToManyRelationships"); | ||
module.exports = function PgManyToManyRelationPlugin(builder, options) { | ||
const { pgSimpleCollections } = options; | ||
builder.hook("GraphQLObjectType:fields", (fields, build, context) => { | ||
const { | ||
extend, | ||
getTypeByName, | ||
pgGetGqlTypeByTypeIdAndModifier, | ||
pgIntrospectionResultsByKind: introspectionResultsByKind, | ||
pgSql: sql, | ||
@@ -71,3 +17,2 @@ getSafeAliasFromResolveInfo, | ||
pgAddStartEndCursor: addStartEndCursor, | ||
pgOmit: omit, | ||
describePgEntity, | ||
@@ -84,299 +29,200 @@ } = build; | ||
const relationships = manyToManyRelationships(leftTable, build); | ||
return extend( | ||
fields, | ||
leftTable.foreignConstraints | ||
.filter(con => con.type === "f") | ||
.reduce((memoLeft, junctionLeftConstraint) => { | ||
if ( | ||
omit(junctionLeftConstraint, "read") || | ||
omit(junctionLeftConstraint, "manyToMany") | ||
) { | ||
return memoLeft; | ||
} | ||
const junctionTable = | ||
introspectionResultsByKind.classById[ | ||
junctionLeftConstraint.classId | ||
]; | ||
if (!junctionTable) { | ||
throw new Error( | ||
`Could not find the table that referenced us (constraint: ${ | ||
junctionLeftConstraint.name | ||
})` | ||
); | ||
} | ||
if (omit(junctionTable, "manyToMany")) { | ||
return memoLeft; | ||
} | ||
memoLeft = extend( | ||
memoLeft, | ||
junctionTable.constraints | ||
.filter( | ||
con => | ||
con.id !== junctionLeftConstraint.id && // Don't follow the same constraint back to the left table | ||
con.type === "f" && | ||
!omit(con, "read") && | ||
!omit(con, "manyToMany") | ||
) | ||
.reduce((memoRight, junctionRightConstraint) => { | ||
const rightTable = junctionRightConstraint.foreignClass; | ||
const rightTableTypeName = inflection.tableType(rightTable); | ||
const RightTableType = pgGetGqlTypeByTypeIdAndModifier( | ||
rightTable.type.id, | ||
null | ||
); | ||
if (!RightTableType) { | ||
throw new Error( | ||
`Could not determine type for table with id ${ | ||
junctionRightConstraint.classId | ||
}` | ||
); | ||
} | ||
const RightTableConnectionType = getTypeByName( | ||
inflection.connection(RightTableType.name) | ||
); | ||
relationships.reduce((memo, relationship) => { | ||
const { | ||
leftKeyAttributes, | ||
junctionLeftKeyAttributes, | ||
junctionRightKeyAttributes, | ||
rightKeyAttributes, | ||
junctionTable, | ||
rightTable, | ||
junctionLeftConstraint, | ||
junctionRightConstraint, | ||
} = relationship; | ||
const RightTableType = pgGetGqlTypeByTypeIdAndModifier( | ||
rightTable.type.id, | ||
null | ||
); | ||
if (!RightTableType) { | ||
throw new Error( | ||
`Could not determine type for table with id ${rightTable.type.id}` | ||
); | ||
} | ||
const RightTableConnectionType = createManyToManyConnectionType( | ||
relationship, | ||
build, | ||
options, | ||
leftTable | ||
); | ||
const leftKeyAttributes = | ||
junctionLeftConstraint.foreignKeyAttributes; | ||
const junctionLeftKeyAttributes = | ||
junctionLeftConstraint.keyAttributes; | ||
const junctionRightKeyAttributes = | ||
junctionRightConstraint.keyAttributes; | ||
const rightKeyAttributes = | ||
junctionRightConstraint.foreignKeyAttributes; | ||
// Since we're ignoring multi-column keys, we can simplify here | ||
const leftKeyAttribute = leftKeyAttributes[0]; | ||
const junctionLeftKeyAttribute = junctionLeftKeyAttributes[0]; | ||
const junctionRightKeyAttribute = junctionRightKeyAttributes[0]; | ||
const rightKeyAttribute = rightKeyAttributes[0]; | ||
// Ensure keys were found | ||
if ( | ||
!leftKeyAttributes.every(_ => _) || | ||
!junctionLeftKeyAttributes.every(_ => _) || | ||
!junctionRightKeyAttributes.every(_ => _) || | ||
!rightKeyAttributes.every(_ => _) | ||
) { | ||
throw new Error("Could not find key columns!"); | ||
} | ||
function makeFields(isConnection) { | ||
const manyRelationFieldName = isConnection | ||
? inflection.manyToManyRelationByKeys( | ||
leftKeyAttributes, | ||
junctionLeftKeyAttributes, | ||
junctionRightKeyAttributes, | ||
rightKeyAttributes, | ||
junctionTable, | ||
rightTable, | ||
junctionLeftConstraint, | ||
junctionRightConstraint | ||
) | ||
: inflection.manyToManyRelationByKeysSimple( | ||
leftKeyAttributes, | ||
junctionLeftKeyAttributes, | ||
junctionRightKeyAttributes, | ||
rightKeyAttributes, | ||
junctionTable, | ||
rightTable, | ||
junctionLeftConstraint, | ||
junctionRightConstraint | ||
); | ||
// Ensure keys can be read | ||
if ( | ||
leftKeyAttributes.some(attr => omit(attr, "read")) || | ||
junctionLeftKeyAttributes.some(attr => omit(attr, "read")) || | ||
junctionRightKeyAttributes.some(attr => omit(attr, "read")) || | ||
rightKeyAttributes.some(attr => omit(attr, "read")) | ||
) { | ||
return memoRight; | ||
} | ||
// Ensure both constraints are single-column | ||
// TODO: handle multi-column | ||
if ( | ||
leftKeyAttributes.length > 1 || | ||
rightKeyAttributes.length > 1 | ||
) { | ||
return memoRight; | ||
} | ||
// Since we're ignoring multi-column keys, we can simplify here | ||
const leftKeyAttribute = leftKeyAttributes[0]; | ||
const junctionLeftKeyAttribute = junctionLeftKeyAttributes[0]; | ||
const junctionRightKeyAttribute = junctionRightKeyAttributes[0]; | ||
const rightKeyAttribute = rightKeyAttributes[0]; | ||
// Ensure junction constraint keys are not unique (which would result in a one-to-one relation) | ||
const junctionLeftConstraintIsUnique = !!junctionTable.constraints.find( | ||
c => | ||
(c.type === "p" || c.type === "u") && | ||
c.keyAttributeNums.length === | ||
junctionLeftKeyAttributes.length && | ||
c.keyAttributeNums.every( | ||
(n, i) => junctionLeftKeyAttributes[i].num === n | ||
) | ||
); | ||
const junctionRightConstraintIsUnique = !!junctionTable.constraints.find( | ||
c => | ||
(c.type === "p" || c.type === "u") && | ||
c.keyAttributeNums.length === | ||
junctionRightKeyAttributes.length && | ||
c.keyAttributeNums.every( | ||
(n, i) => junctionRightKeyAttributes[i].num === n | ||
) | ||
); | ||
if ( | ||
junctionLeftConstraintIsUnique || | ||
junctionRightConstraintIsUnique | ||
) { | ||
return memoRight; | ||
} | ||
function makeFields(isConnection) { | ||
const manyRelationFieldName = isConnection | ||
? inflection.manyToManyRelationByKeys( | ||
leftKeyAttributes, | ||
junctionLeftKeyAttributes, | ||
junctionRightKeyAttributes, | ||
rightKeyAttributes, | ||
junctionTable, | ||
rightTable, | ||
junctionLeftConstraint, | ||
junctionRightConstraint | ||
) | ||
: inflection.manyToManyRelationByKeysSimple( | ||
leftKeyAttributes, | ||
junctionLeftKeyAttributes, | ||
junctionRightKeyAttributes, | ||
rightKeyAttributes, | ||
junctionTable, | ||
rightTable, | ||
junctionLeftConstraint, | ||
junctionRightConstraint | ||
); | ||
memoRight = extend( | ||
memoRight, | ||
{ | ||
[manyRelationFieldName]: fieldWithHooks( | ||
manyRelationFieldName, | ||
({ | ||
getDataFromParsedResolveInfoFragment, | ||
addDataGenerator, | ||
}) => { | ||
addDataGenerator(parsedResolveInfoFragment => { | ||
return { | ||
pgQuery: queryBuilder => { | ||
queryBuilder.select(() => { | ||
const resolveData = getDataFromParsedResolveInfoFragment( | ||
parsedResolveInfoFragment, | ||
isConnection | ||
? RightTableConnectionType | ||
: RightTableType | ||
); | ||
const rightTableAlias = sql.identifier( | ||
Symbol() | ||
); | ||
const leftTableAlias = queryBuilder.getTableAlias(); | ||
const query = queryFromResolveData( | ||
sql.identifier( | ||
rightTable.namespace.name, | ||
rightTable.name | ||
), | ||
rightTableAlias, | ||
resolveData, | ||
{ | ||
withPagination: isConnection, | ||
withPaginationAsFields: false, | ||
asJsonAggregate: !isConnection, | ||
}, | ||
innerQueryBuilder => { | ||
innerQueryBuilder.parentQueryBuilder = queryBuilder; | ||
const rightPrimaryKeyConstraint = | ||
rightTable.primaryKeyConstraint; | ||
const rightPrimaryKeyAttributes = | ||
rightPrimaryKeyConstraint && | ||
rightPrimaryKeyConstraint.keyAttributes; | ||
if (rightPrimaryKeyAttributes) { | ||
innerQueryBuilder.beforeLock( | ||
"orderBy", | ||
() => { | ||
// append order by primary key to the list of orders | ||
if ( | ||
!innerQueryBuilder.isOrderUnique( | ||
false | ||
) | ||
) { | ||
innerQueryBuilder.data.cursorPrefix = [ | ||
"primary_key_asc", | ||
]; | ||
rightPrimaryKeyAttributes.forEach( | ||
attr => { | ||
innerQueryBuilder.orderBy( | ||
sql.fragment`${innerQueryBuilder.getTableAlias()}.${sql.identifier( | ||
attr.name | ||
)}`, | ||
true | ||
); | ||
} | ||
); | ||
innerQueryBuilder.setOrderIsUnique(); | ||
} | ||
} | ||
); | ||
} | ||
innerQueryBuilder.where( | ||
sql.fragment`${rightTableAlias}.${sql.identifier( | ||
rightKeyAttribute.name | ||
)} in (select ${sql.identifier( | ||
junctionRightKeyAttribute.name | ||
)} from ${sql.identifier( | ||
junctionTable.namespace.name, | ||
junctionTable.name | ||
)} where ${sql.identifier( | ||
junctionLeftKeyAttribute.name | ||
)} = ${leftTableAlias}.${sql.identifier( | ||
leftKeyAttribute.name | ||
)})` | ||
memo = extend( | ||
memo, | ||
{ | ||
[manyRelationFieldName]: fieldWithHooks( | ||
manyRelationFieldName, | ||
({ | ||
getDataFromParsedResolveInfoFragment, | ||
addDataGenerator, | ||
}) => { | ||
const sqlFrom = sql.identifier( | ||
rightTable.namespace.name, | ||
rightTable.name | ||
); | ||
const queryOptions = { | ||
useAsterisk: rightTable.canUseAsterisk, | ||
withPagination: isConnection, | ||
withPaginationAsFields: false, | ||
asJsonAggregate: !isConnection, | ||
}; | ||
addDataGenerator(parsedResolveInfoFragment => { | ||
return { | ||
pgQuery: queryBuilder => { | ||
queryBuilder.select(() => { | ||
const resolveData = getDataFromParsedResolveInfoFragment( | ||
parsedResolveInfoFragment, | ||
isConnection | ||
? RightTableConnectionType | ||
: RightTableType | ||
); | ||
const rightTableAlias = sql.identifier(Symbol()); | ||
const leftTableAlias = queryBuilder.getTableAlias(); | ||
const query = queryFromResolveData( | ||
sqlFrom, | ||
rightTableAlias, | ||
resolveData, | ||
queryOptions, | ||
innerQueryBuilder => { | ||
innerQueryBuilder.parentQueryBuilder = queryBuilder; | ||
const rightPrimaryKeyConstraint = | ||
rightTable.primaryKeyConstraint; | ||
const rightPrimaryKeyAttributes = | ||
rightPrimaryKeyConstraint && | ||
rightPrimaryKeyConstraint.keyAttributes; | ||
if (rightPrimaryKeyAttributes) { | ||
innerQueryBuilder.beforeLock("orderBy", () => { | ||
// append order by primary key to the list of orders | ||
if (!innerQueryBuilder.isOrderUnique(false)) { | ||
innerQueryBuilder.data.cursorPrefix = [ | ||
"primary_key_asc", | ||
]; | ||
rightPrimaryKeyAttributes.forEach(attr => { | ||
innerQueryBuilder.orderBy( | ||
sql.fragment`${innerQueryBuilder.getTableAlias()}.${sql.identifier( | ||
attr.name | ||
)}`, | ||
true | ||
); | ||
}, | ||
queryBuilder.context, | ||
queryBuilder.rootValue | ||
); | ||
return sql.fragment`(${query})`; | ||
}, getSafeAliasFromAlias(parsedResolveInfoFragment.alias)); | ||
}, | ||
}; | ||
}); | ||
}); | ||
innerQueryBuilder.setOrderIsUnique(); | ||
} | ||
}); | ||
} | ||
return { | ||
description: `Reads and enables pagination through a set of \`${rightTableTypeName}\`.`, | ||
type: isConnection | ||
? new GraphQLNonNull(RightTableConnectionType) | ||
: new GraphQLNonNull( | ||
new GraphQLList( | ||
new GraphQLNonNull(RightTableType) | ||
) | ||
), | ||
args: {}, | ||
resolve: (data, _args, _context, resolveInfo) => { | ||
const safeAlias = getSafeAliasFromResolveInfo( | ||
resolveInfo | ||
innerQueryBuilder.where( | ||
sql.fragment`${rightTableAlias}.${sql.identifier( | ||
rightKeyAttribute.name | ||
)} in (select ${sql.identifier( | ||
junctionRightKeyAttribute.name | ||
)} from ${sql.identifier( | ||
junctionTable.namespace.name, | ||
junctionTable.name | ||
)} where ${sql.identifier( | ||
junctionLeftKeyAttribute.name | ||
)} = ${leftTableAlias}.${sql.identifier( | ||
leftKeyAttribute.name | ||
)})` | ||
); | ||
if (isConnection) { | ||
return addStartEndCursor(data[safeAlias]); | ||
} else { | ||
return data[safeAlias]; | ||
} | ||
}, | ||
}; | ||
}, | ||
{ | ||
isPgFieldConnection: isConnection, | ||
isPgFieldSimpleCollection: !isConnection, | ||
isPgManyToManyRelationField: true, | ||
pgFieldIntrospection: rightTable, | ||
} | ||
), | ||
queryBuilder.context, | ||
queryBuilder.rootValue | ||
); | ||
return sql.fragment`(${query})`; | ||
}, getSafeAliasFromAlias(parsedResolveInfoFragment.alias)); | ||
}, | ||
}; | ||
}); | ||
return { | ||
description: `Reads and enables pagination through a set of \`${RightTableType.name}\`.`, | ||
type: isConnection | ||
? new GraphQLNonNull(RightTableConnectionType) | ||
: new GraphQLNonNull( | ||
new GraphQLList(new GraphQLNonNull(RightTableType)) | ||
), | ||
args: {}, | ||
resolve: (data, _args, _context, resolveInfo) => { | ||
const safeAlias = getSafeAliasFromResolveInfo( | ||
resolveInfo | ||
); | ||
if (isConnection) { | ||
return addStartEndCursor(data[safeAlias]); | ||
} else { | ||
return data[safeAlias]; | ||
} | ||
}, | ||
}; | ||
}, | ||
{ | ||
isPgFieldConnection: isConnection, | ||
isPgFieldSimpleCollection: !isConnection, | ||
isPgManyToManyRelationField: true, | ||
pgFieldIntrospection: rightTable, | ||
} | ||
), | ||
}, | ||
`Many-to-many relation field (${ | ||
isConnection ? "connection" : "simple collection" | ||
}) on ${Self.name} type for ${describePgEntity( | ||
junctionLeftConstraint | ||
)} and ${describePgEntity(junctionRightConstraint)}.` | ||
); | ||
} | ||
const simpleCollections = | ||
junctionRightConstraint.tags.simpleCollections || | ||
rightTable.tags.simpleCollections || | ||
pgSimpleCollections; | ||
const hasConnections = simpleCollections !== "only"; | ||
const hasSimpleCollections = | ||
simpleCollections === "only" || simpleCollections === "both"; | ||
if (hasConnections) { | ||
makeFields(true); | ||
} | ||
if (hasSimpleCollections) { | ||
makeFields(false); | ||
} | ||
return memoRight; | ||
}, {}) | ||
`Many-to-many relation field (${ | ||
isConnection ? "connection" : "simple collection" | ||
}) on ${Self.name} type for ${describePgEntity( | ||
junctionLeftConstraint | ||
)} and ${describePgEntity(junctionRightConstraint)}.` | ||
); | ||
return memoLeft; | ||
}, {}), | ||
} | ||
const simpleCollections = | ||
junctionRightConstraint.tags.simpleCollections || | ||
rightTable.tags.simpleCollections || | ||
pgSimpleCollections; | ||
const hasConnections = simpleCollections !== "only"; | ||
const hasSimpleCollections = | ||
simpleCollections === "only" || simpleCollections === "both"; | ||
if (hasConnections) { | ||
makeFields(true); | ||
} | ||
if (hasSimpleCollections) { | ||
makeFields(false); | ||
} | ||
return memo; | ||
}, {}), | ||
`Adding many-to-many relations for ${Self.name}` | ||
@@ -383,0 +229,0 @@ ); |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
46315
10
1040
1