-
-
Notifications
You must be signed in to change notification settings - Fork 568
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(graphql): add fields based on unique constraints
Closes #67
- Loading branch information
Showing
13 changed files
with
221 additions
and
93 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { memoize } from 'lodash' | ||
import { GraphQLNonNull, GraphQLID } from 'graphql' | ||
import { $$isViewer } from '../../symbols.js' | ||
import { NodeType, fromID } from '../types.js' | ||
import resolveTableSingle from '../resolveTableSingle.js' | ||
|
||
const createAllNodeQueryField = schema => { | ||
const getTable = memoize(tableName => schema.catalog.getTable(schema.name, tableName)) | ||
return { | ||
type: NodeType, | ||
description: 'Fetches an object given its globally unique `ID`.', | ||
|
||
args: { | ||
id: { | ||
type: new GraphQLNonNull(GraphQLID), | ||
description: 'The `ID` of the node.', | ||
}, | ||
}, | ||
|
||
resolve: (source, args, context) => { | ||
const { id } = args | ||
|
||
// If the id is just `viewer`, we are trying to refetch the viewer node. | ||
if (id === 'viewer') | ||
return { [$$isViewer]: true } | ||
|
||
const { tableName, values } = fromID(id) | ||
const table = getTable(tableName) | ||
|
||
if (!table) | ||
throw new Error(`No table '${tableName}' in schema '${schema.name}'.`) | ||
|
||
return getResolveNode(table)({ values }, {}, context) | ||
}, | ||
} | ||
} | ||
|
||
export default createAllNodeQueryField | ||
|
||
// This function will be called for every resolution, therefore it is (and | ||
// must be) memoized. | ||
// | ||
// Because this is memoized, fetching primary keys is ok here. | ||
const getResolveNode = memoize(table => resolveTableSingle( | ||
table, | ||
table.getPrimaryKeys(), | ||
({ values }) => values, | ||
)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,42 @@ | ||
import { memoize } from 'lodash' | ||
import { GraphQLNonNull, GraphQLID } from 'graphql' | ||
import { $$isViewer } from '../../symbols.js' | ||
import { NodeType, fromID } from '../types.js' | ||
import { fromID } from '../types.js' | ||
import createTableType from '../createTableType.js' | ||
import resolveTableSingle from '../resolveTableSingle.js' | ||
|
||
const createNodeQueryField = schema => { | ||
const getTable = memoize(tableName => schema.catalog.getTable(schema.name, tableName)) | ||
/** | ||
* Creates an object field for selecting a single row of a table. | ||
* | ||
* @param {Table} table | ||
* @returns {GraphQLFieldConfig} | ||
*/ | ||
const createNodeQueryField = table => { | ||
const primaryKeys = table.getPrimaryKeys() | ||
|
||
// Can’t query a single node of a table if it does not have a primary key. | ||
if (primaryKeys.length === 0) | ||
return null | ||
|
||
return { | ||
type: NodeType, | ||
description: 'Fetches an object given its globally unique `ID`.', | ||
type: createTableType(table), | ||
description: `Queries a single ${table.getMarkdownTypeName()} using its primary keys.`, | ||
|
||
args: { | ||
id: { | ||
type: new GraphQLNonNull(GraphQLID), | ||
description: 'The `ID` of the node.', | ||
description: `The \`ID\` of the ${table.getMarkdownTypeName()} node.`, | ||
}, | ||
}, | ||
|
||
resolve: (source, args, context) => { | ||
const { id } = args | ||
|
||
// If the id is just `viewer`, we are trying to refetch the viewer node. | ||
if (id === 'viewer') | ||
return { [$$isViewer]: true } | ||
|
||
const { tableName, values } = fromID(id) | ||
const table = getTable(tableName) | ||
|
||
if (!table) | ||
throw new Error(`No table '${tableName}' in schema '${schema.name}'.`) | ||
|
||
return getResolveNode(table)({ values }, {}, context) | ||
}, | ||
resolve: resolveTableSingle( | ||
table, | ||
primaryKeys, | ||
(source, { id }) => { | ||
const { tableName, values } = fromID(id) | ||
if (tableName !== table.name) return null | ||
return values | ||
} | ||
), | ||
} | ||
} | ||
|
||
export default createNodeQueryField | ||
|
||
// This function will be called for every resolution, therefore it is (and | ||
// must be) memoized. | ||
// | ||
// Because this is memoized, fetching primary keys is ok here. | ||
const getResolveNode = memoize(table => resolveTableSingle( | ||
table, | ||
table.getPrimaryKeys(), | ||
({ values }) => values, | ||
)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,28 @@ | ||
import { GraphQLNonNull, GraphQLID } from 'graphql' | ||
import { fromID } from '../types.js' | ||
import { fromPairs } from 'lodash' | ||
import { GraphQLNonNull, getNullableType } from 'graphql' | ||
import createTableType from '../createTableType.js' | ||
import getColumnType from '../getColumnType.js' | ||
import resolveTableSingle from '../resolveTableSingle.js' | ||
|
||
/** | ||
* Creates an object field for selecting a single row of a table. | ||
* | ||
* @param {Table} table | ||
* @returns {GraphQLFieldConfig} | ||
*/ | ||
const createSingleQueryField = table => { | ||
const primaryKeys = table.getPrimaryKeys() | ||
const createSingleQueryField = (table, columns) => ({ | ||
type: createTableType(table), | ||
description: | ||
`Queries a single ${table.getMarkdownTypeName()} using a uniqueness ` + | ||
`constraint with field${columns.length === 1 ? '' : 's'} ` + | ||
`${columns.map(column => column.getMarkdownFieldName()).join(', ')}.`, | ||
|
||
// Can’t query a single node of a table if it does not have a primary key. | ||
if (primaryKeys.length === 0) | ||
return null | ||
args: fromPairs( | ||
columns.map(column => [column.getFieldName(), { | ||
type: new GraphQLNonNull(getNullableType(getColumnType(column))), | ||
description: `The exact value of the ${column.getMarkdownFieldName()} field to match.`, | ||
}]) | ||
), | ||
|
||
return { | ||
type: createTableType(table), | ||
description: `Queries a single ${table.getMarkdownTypeName()} using its primary keys.`, | ||
|
||
args: { | ||
id: { | ||
type: new GraphQLNonNull(GraphQLID), | ||
description: `The \`ID\` of the ${table.getMarkdownTypeName()} node.`, | ||
}, | ||
}, | ||
|
||
resolve: resolveTableSingle( | ||
table, | ||
primaryKeys, | ||
(source, { id }) => { | ||
const { tableName, values } = fromID(id) | ||
if (tableName !== table.name) return null | ||
return values | ||
} | ||
), | ||
} | ||
} | ||
resolve: resolveTableSingle( | ||
table, | ||
columns, | ||
(source, args) => columns.map(column => args[column.getFieldName()]) | ||
), | ||
}) | ||
|
||
export default createSingleQueryField |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
query UniqueConstraints { | ||
thingByRowId(rowId: 1) { ...thing } | ||
relationByAThingIdAndBThingId(aThingId: 1, bThingId: 2) { | ||
id | ||
aThingId | ||
bThingId | ||
thingByAThingId { ...thing } | ||
thingByBThingId { ...thing } | ||
} | ||
thingByLuckyNumber(luckyNumber: 98) { ...thing } | ||
} | ||
|
||
fragment thing on Thing { | ||
id | ||
rowId | ||
note | ||
} |
Oops, something went wrong.