Skip to content

Commit

Permalink
feat(postgraphql): add head to tail relation selection
Browse files Browse the repository at this point in the history
  • Loading branch information
calebmer committed Oct 9, 2016
1 parent 31b7e1e commit 464ba71
Show file tree
Hide file tree
Showing 7 changed files with 497 additions and 52 deletions.
46 changes: 25 additions & 21 deletions src/graphql/schema/collection/getCollectionGQLType.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { GraphQLObjectType, GraphQLFieldConfig, GraphQLNonNull, GraphQLID, GraphQLOutputType } from 'graphql'
import { Collection, ObjectType, Relation } from '../../../interface'
import { Collection, Condition, ObjectType, Relation } from '../../../interface'
import { memoize2, formatName, buildObject, idSerde, scrib } from '../../utils'
import getNodeInterfaceType from '../node/getNodeInterfaceType'
import getGQLType from '../getGQLType'
Expand Down Expand Up @@ -76,28 +76,32 @@ function createCollectionGQLType (buildToken: BuildToken, collection: Collection
// Add fields from relations where this collection is the tail.
createCollectionRelationTailGQLFieldEntries(buildToken, collection),

// // Add all of our one-to-many relations (aka head relations).
// inventory.getRelations()
// // We only want the relations for which this collection is the head
// // collection.
// .filter(relation => relation.getHeadCollectionKey().getCollection() === collection)
// // Transform the relation into a field entry.
// .map(<TTailValue, TKey>(relation: Relation<TTailValue, TValue, TKey>): [string, GraphQLFieldConfig<TValue, any>] | undefined => {
// const tailCollection = relation.getTailCollection()
// const tailPaginator = relation.getTailPaginator()
// Add all of our one-to-many relations (aka head relations).
inventory.getRelations()
// We only want the relations for which this collection is the head
// collection.
.filter(relation => relation.headCollectionKey.collection === collection)
// Transform the relation into a field entry.
.map(<THeadKey>(relation: Relation<THeadKey>): [string, GraphQLFieldConfig<ObjectType.Value, any>] | null => {
const tailCollection = relation.tailCollection
const tailPaginator = tailCollection.paginator

// // TODO: This shouldn’t be optional?
// if (!tailPaginator) return undefined
// If there is no tail paginator, or the relation cannot get a
// condition for that paginator we can’t provide this field so
// return null.
if (!tailPaginator || !relation.getTailConditionFromHeadValue)
return null

// return [
// formatName.field(`${tailCollection.getName()}-by-${relation.getName()}`),
// createConnectionGQLField(buildToken, tailPaginator, {
// // We use the config when creating a connection field to inject
// // a condition that limits what we select from the paginator.
// getCondition: (headValue: TValue) => relation.getTailConditionFromHeadValue(headValue),
// }),
// ]
// }),
return [
formatName.field(`${tailCollection.name}-by-${relation.name}`),
createConnectionGQLField<ObjectType.Value, Condition, ObjectType.Value>(buildToken, tailPaginator, {
// We use the config when creating a connection field to inject
// a condition that limits what we select from the paginator.
getPaginatorInput: headValue =>
relation.getTailConditionFromHeadValue!(headValue),
}),
]
}),
),
})
}
1 change: 1 addition & 0 deletions src/interface/collection/Condition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// TODO: Consider a text search condition.
// TODO: Consider some geographic operators.
// TODO: Consider some array operators.
// TODO: REFACTOR!!!! The fact that this isn’t type safe is a little scary…
type Condition =
ConstantCondition |
NotCondition |
Expand Down
37 changes: 10 additions & 27 deletions src/interface/collection/Relation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import Condition from './Condition'
*
* @see https://en.wikipedia.org/wiki/Directed_graph#Basic_terminology
*/
interface Relation<TKey> {
interface Relation<THeadKey> {
/**
* The name of the relation.
*/
Expand All @@ -45,38 +45,21 @@ interface Relation<TKey> {
/**
* The head collection key in this relationship.
*/
readonly headCollectionKey: CollectionKey<TKey>
readonly headCollectionKey: CollectionKey<THeadKey>

/**
* Gets the key for a value in the head collection from the tail collection
* value. This allows us to see the “one” in a many-to-one mental model.
*/
getHeadKeyFromTailValue (value: ObjectType.Value): TKey
getHeadKeyFromTailValue (value: ObjectType.Value): THeadKey

// /**
// * Gets the paginator for values in the tail collection which we will use
// * with a condition from `Relation#getTailConditionFromHeadValue`.
// *
// * The reason we have two seperate methods (one for the paginator, one for
// * the condition) is that we need to statically know information about the
// * paginator. Such as orderings, type, and name information.
// *
// * @see Relation#getTailConditionFromHeadValue
// */
// // TODO: Is this really the right way to do this?
// public getTailPaginator (): Paginator<TTailValue, mixed> | undefined {
// return this._tailPaginator
// }

// /**
// * Gets a condition which will be used with the tail paginator from
// * `Relation#getTailPaginator` to select all of the tail values from the
// * single head value. This allows us to see the “many” in a one-to-many
// * mental model.
// *
// * @see Relation#getTailPaginator
// */
// public abstract getTailConditionFromHeadValue (value: THeadValue): Condition
/**
* Gets a condition that can be used with the tail collection’s paginator to
* select all of the tail values from the single head value. This allows us
* to see the “many” in a one-to-many model.
*/
// TODO: REFACTOR
getTailConditionFromHeadValue? (value: ObjectType.Value): Condition
}

export default Relation
Original file line number Diff line number Diff line change
Expand Up @@ -1490,6 +1490,233 @@ Object {
}
`;

exports[`test operation relation-head-tail.graphql 1`] = `
Object {
"data": Object {
"allCompoundKeys": Object {
"nodes": Array [
Object {
"foreignKeysByCompoundKey1AndCompoundKey2": Object {
"nodes": Array [],
},
"personId1": 1,
"personId2": 2,
},
Object {
"foreignKeysByCompoundKey1AndCompoundKey2": Object {
"nodes": Array [
Object {
"compoundKey1": 2,
"compoundKey2": 1,
"personId": 5,
},
Object {
"compoundKey1": 2,
"compoundKey2": 1,
"personId": null,
},
],
},
"personId1": 2,
"personId2": 1,
},
Object {
"foreignKeysByCompoundKey1AndCompoundKey2": Object {
"nodes": Array [],
},
"personId1": 2,
"personId2": 3,
},
Object {
"foreignKeysByCompoundKey1AndCompoundKey2": Object {
"nodes": Array [],
},
"personId1": 2,
"personId2": 5,
},
Object {
"foreignKeysByCompoundKey1AndCompoundKey2": Object {
"nodes": Array [],
},
"personId1": 4,
"personId2": 3,
},
Object {
"foreignKeysByCompoundKey1AndCompoundKey2": Object {
"nodes": Array [
Object {
"compoundKey1": 4,
"compoundKey2": 4,
"personId": null,
},
],
},
"personId1": 4,
"personId2": 4,
},
],
},
"allPeople": Object {
"nodes": Array [
Object {
"compoundKeysByPersonId1": Object {
"nodes": Array [
Object {
"personId1": 1,
"personId2": 2,
},
],
},
"compoundKeysByPersonId2": Object {
"nodes": Array [
Object {
"personId1": 2,
"personId2": 1,
},
],
},
"id": 1,
"name": "John Smith",
"postsByAuthorId": Object {
"nodes": Array [
Object {
"authorId": 1,
"headline": "You hit me with a cricket bat.",
},
Object {
"authorId": 1,
"headline": "Large bet on myself in round one.",
},
],
},
},
Object {
"compoundKeysByPersonId1": Object {
"nodes": Array [
Object {
"personId1": 2,
"personId2": 1,
},
Object {
"personId1": 2,
"personId2": 3,
},
Object {
"personId1": 2,
"personId2": 5,
},
],
},
"compoundKeysByPersonId2": Object {
"nodes": Array [
Object {
"personId1": 1,
"personId2": 2,
},
],
},
"id": 2,
"name": "Sara Smith",
"postsByAuthorId": Object {
"nodes": Array [
Object {
"authorId": 2,
"headline": "It’s a fez. I wear a fez now. Fezes are cool.",
},
Object {
"authorId": 2,
"headline": "What’s with you kids? Every other day it’s food, food, food.",
},
],
},
},
Object {
"compoundKeysByPersonId1": Object {
"nodes": Array [],
},
"compoundKeysByPersonId2": Object {
"nodes": Array [
Object {
"personId1": 2,
"personId2": 3,
},
Object {
"personId1": 4,
"personId2": 3,
},
],
},
"id": 3,
"name": "Budd Deey",
"postsByAuthorId": Object {
"nodes": Array [
Object {
"authorId": 3,
"headline": "You know how I sometimes have really brilliant ideas?",
},
Object {
"authorId": 3,
"headline": "They’re not aliens, they’re Earth…liens!",
},
],
},
},
Object {
"compoundKeysByPersonId1": Object {
"nodes": Array [
Object {
"personId1": 4,
"personId2": 3,
},
Object {
"personId1": 4,
"personId2": 4,
},
],
},
"compoundKeysByPersonId2": Object {
"nodes": Array [
Object {
"personId1": 4,
"personId2": 4,
},
],
},
"id": 4,
"name": "Kathryn Ramirez",
"postsByAuthorId": Object {
"nodes": Array [],
},
},
Object {
"compoundKeysByPersonId1": Object {
"nodes": Array [],
},
"compoundKeysByPersonId2": Object {
"nodes": Array [
Object {
"personId1": 2,
"personId2": 5,
},
],
},
"id": 5,
"name": "Johnny Tucker",
"postsByAuthorId": Object {
"nodes": Array [
Object {
"authorId": 5,
"headline": "Please, Don-Bot… look into your hard drive, and open your mercy file!",
},
],
},
},
],
},
},
}
`;

exports[`test operation relation-tail-head.graphql 1`] = `
Object {
"data": Object {
Expand Down
Loading

0 comments on commit 464ba71

Please sign in to comment.