mirror of
https://github.com/BradNut/graphbrainz
synced 2025-09-08 17:40:32 +00:00
More resolveType fixes, this time for Relay's nodeDefinitions (#51)
* More resolveType fixes, this time for Relay's nodeDefinitions * Add a loadExtension helper * Use createContext in test helpers
This commit is contained in:
parent
2de2e60079
commit
c3be2a2e98
18 changed files with 236 additions and 82 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import createLoaders from './loaders'
|
||||
import { loadExtension } from './extensions'
|
||||
|
||||
const debug = require('debug')('graphbrainz:context')
|
||||
|
||||
|
|
@ -27,6 +28,6 @@ export function createContext(options = {}) {
|
|||
const context = { client, loaders }
|
||||
const { extensions = [] } = options
|
||||
return extensions.reduce((context, extension) => {
|
||||
return extendContext(extension, context, options)
|
||||
return extendContext(loadExtension(extension), context, options)
|
||||
}, context)
|
||||
}
|
||||
|
|
|
|||
18
src/extensions/index.js
Normal file
18
src/extensions/index.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
export function loadExtension(extensionModule) {
|
||||
let extension
|
||||
if (typeof extensionModule === 'string') {
|
||||
extension = require(extensionModule)
|
||||
} else {
|
||||
extension = extensionModule
|
||||
}
|
||||
if (extension == null || typeof extension !== 'object') {
|
||||
throw new Error(
|
||||
`Expected ${extensionModule} to export an extension but instead ` +
|
||||
`got: ${extension}`
|
||||
)
|
||||
} else if (extension.default) {
|
||||
// ECMAScript module interop.
|
||||
extension = extension.default
|
||||
}
|
||||
return extension
|
||||
}
|
||||
29
src/index.js
29
src/index.js
|
|
@ -6,40 +6,29 @@ import MusicBrainz from './api'
|
|||
import schema, { createSchema } from './schema'
|
||||
import { createContext } from './context'
|
||||
|
||||
const debug = require('debug')('graphbrainz')
|
||||
|
||||
const formatError = err => ({
|
||||
message: err.message,
|
||||
locations: err.locations,
|
||||
stack: err.stack
|
||||
})
|
||||
|
||||
export const defaultExtensions = [
|
||||
require.resolve('./extensions/cover-art-archive'),
|
||||
require.resolve('./extensions/fanart-tv'),
|
||||
require.resolve('./extensions/mediawiki'),
|
||||
require.resolve('./extensions/the-audio-db')
|
||||
]
|
||||
|
||||
const middleware = (
|
||||
{
|
||||
client = new MusicBrainz(),
|
||||
extensions = process.env.GRAPHBRAINZ_EXTENSIONS
|
||||
? JSON.parse(process.env.GRAPHBRAINZ_EXTENSIONS)
|
||||
: [
|
||||
'./extensions/cover-art-archive',
|
||||
'./extensions/fanart-tv',
|
||||
'./extensions/mediawiki',
|
||||
'./extensions/the-audio-db'
|
||||
],
|
||||
: defaultExtensions,
|
||||
...middlewareOptions
|
||||
} = {}
|
||||
) => {
|
||||
debug(`Loading ${extensions.length} extension(s).`)
|
||||
const options = {
|
||||
client,
|
||||
extensions: extensions.map(extensionModule => {
|
||||
if (typeof extensionModule === 'object') {
|
||||
return extensionModule
|
||||
}
|
||||
const extension = require(extensionModule)
|
||||
return extension.default ? extension.default : extension
|
||||
}),
|
||||
...middlewareOptions
|
||||
}
|
||||
const options = { client, extensions, ...middlewareOptions }
|
||||
const DEV = process.env.NODE_ENV !== 'production'
|
||||
const graphiql = DEV || process.env.GRAPHBRAINZ_GRAPHIQL === 'true'
|
||||
return graphqlHTTP({
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { GraphQLSchema, GraphQLObjectType, extendSchema, parse } from 'graphql'
|
|||
import { addResolveFunctionsToSchema } from 'graphql-tools'
|
||||
import { lookup, browse, search } from './queries'
|
||||
import { nodeField } from './types/node'
|
||||
import { loadExtension } from './extensions'
|
||||
|
||||
const debug = require('debug')('graphbrainz:schema')
|
||||
|
||||
|
|
@ -45,9 +46,9 @@ export function applyExtension(extension, schema, options = {}) {
|
|||
}
|
||||
|
||||
export function createSchema(schema, options = {}) {
|
||||
const extensions = options.extensions || []
|
||||
const { extensions = [] } = options
|
||||
return extensions.reduce((updatedSchema, extension) => {
|
||||
return applyExtension(extension, updatedSchema, options)
|
||||
return applyExtension(loadExtension(extension), updatedSchema, options)
|
||||
}, schema)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,24 @@
|
|||
import { GraphQLInterfaceType } from 'graphql'
|
||||
import { mbid, connectionWithExtras } from './helpers'
|
||||
|
||||
const debug = require('debug')('graphbrainz:types/entity')
|
||||
|
||||
const Entity = new GraphQLInterfaceType({
|
||||
name: 'Entity',
|
||||
description: 'An entity in the MusicBrainz schema.',
|
||||
resolveType(value, context, info) {
|
||||
if (value._type) {
|
||||
const typeMap = info.schema.getTypeMap()
|
||||
let originalType
|
||||
try {
|
||||
originalType = require(`./${value._type}`).default
|
||||
} catch (err) {
|
||||
debug(`Failed to load type: ${value._type}`)
|
||||
return
|
||||
}
|
||||
// Don't use `originalType`! The schema may have been extended in which
|
||||
// case the types have all been replaced. Instead, find the current type
|
||||
// of the same name.
|
||||
const typeMap = info.schema.getTypeMap()
|
||||
return typeMap[originalType.name]
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -13,13 +13,21 @@ const { nodeInterface, nodeField } = nodeDefinitions(
|
|||
const entityType = toDashed(type)
|
||||
return loaders.lookup.load([entityType, id])
|
||||
},
|
||||
obj => {
|
||||
(obj, context, info) => {
|
||||
const type = TYPE_MODULES[obj._type] || obj._type
|
||||
try {
|
||||
return require(`./${type}`).default
|
||||
} catch (err) {
|
||||
debug(`Failed to load type: ${type}`)
|
||||
return null
|
||||
if (type) {
|
||||
let originalType
|
||||
try {
|
||||
originalType = require(`./${type}`).default
|
||||
} catch (err) {
|
||||
debug(`Failed to load type: ${type}`)
|
||||
return
|
||||
}
|
||||
// Don't use `originalType`! The schema may have been extended in which
|
||||
// case the types have all been replaced. Instead, find the current type
|
||||
// of the same name.
|
||||
const typeMap = info.schema.getTypeMap()
|
||||
return typeMap[originalType.name]
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
import test from 'ava'
|
||||
import { graphql } from 'graphql'
|
||||
import schema from '../src/schema'
|
||||
import schemas from './helpers/schema'
|
||||
import context from './helpers/context'
|
||||
|
||||
const TEST_SCHEMA = process.env.TEST_SCHEMA || 'baseSchema'
|
||||
const schema = schemas[TEST_SCHEMA]
|
||||
|
||||
function testData(t, query, handler) {
|
||||
return graphql(schema, query, null, context).then(result => {
|
||||
if (result.errors !== undefined) {
|
||||
2
test/base-schema.js
Normal file
2
test/base-schema.js
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
process.env.TEST_SCHEMA = 'baseSchema'
|
||||
require('./_schema')
|
||||
2
test/extended-schema.js
Normal file
2
test/extended-schema.js
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
process.env.TEST_SCHEMA = 'extendedSchema'
|
||||
require('./_schema')
|
||||
|
|
@ -1,7 +1,4 @@
|
|||
import createLoaders from '../../src/loaders'
|
||||
import { createContext } from '../../src/context'
|
||||
import client from './client/musicbrainz'
|
||||
|
||||
export default {
|
||||
client,
|
||||
loaders: createLoaders(client)
|
||||
}
|
||||
export default createContext({ client })
|
||||
|
|
|
|||
7
test/helpers/schema.js
Normal file
7
test/helpers/schema.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import schema, { createSchema } from '../../src/schema'
|
||||
import { defaultExtensions } from '../../src'
|
||||
|
||||
export default {
|
||||
baseSchema: schema,
|
||||
extendedSchema: createSchema(schema, { extensions: defaultExtensions })
|
||||
}
|
||||
81
test/snapshots/base-schema.js.md
Normal file
81
test/snapshots/base-schema.js.md
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# Snapshot report for `test/base-schema.js`
|
||||
|
||||
The actual snapshot is saved in `base-schema.js.snap`.
|
||||
|
||||
Generated by [AVA](https://ava.li).
|
||||
|
||||
## areas have a type and typeID
|
||||
|
||||
> Snapshot 1
|
||||
|
||||
{
|
||||
search: {
|
||||
areas: {
|
||||
nodes: [
|
||||
{
|
||||
name: 'Germany',
|
||||
type: 'Country',
|
||||
typeID: '06dd0ae4-8c74-30bb-b43d-95dcedf961de',
|
||||
},
|
||||
{
|
||||
name: 'East Germany',
|
||||
type: 'Country',
|
||||
typeID: '06dd0ae4-8c74-30bb-b43d-95dcedf961de',
|
||||
},
|
||||
{
|
||||
name: 'New Germany',
|
||||
type: 'City',
|
||||
typeID: '6fd8f29a-3d0a-32fc-980d-ea697b69da78',
|
||||
},
|
||||
{
|
||||
name: 'New Germany',
|
||||
type: 'City',
|
||||
typeID: '6fd8f29a-3d0a-32fc-980d-ea697b69da78',
|
||||
},
|
||||
{
|
||||
name: 'Brakel',
|
||||
type: 'City',
|
||||
typeID: '6fd8f29a-3d0a-32fc-980d-ea697b69da78',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
## baseSchema: areas have a type and typeID
|
||||
|
||||
> Snapshot 1
|
||||
|
||||
{
|
||||
search: {
|
||||
areas: {
|
||||
nodes: [
|
||||
{
|
||||
name: 'Germany',
|
||||
type: 'Country',
|
||||
typeID: '06dd0ae4-8c74-30bb-b43d-95dcedf961de',
|
||||
},
|
||||
{
|
||||
name: 'East Germany',
|
||||
type: 'Country',
|
||||
typeID: '06dd0ae4-8c74-30bb-b43d-95dcedf961de',
|
||||
},
|
||||
{
|
||||
name: 'New Germany',
|
||||
type: 'City',
|
||||
typeID: '6fd8f29a-3d0a-32fc-980d-ea697b69da78',
|
||||
},
|
||||
{
|
||||
name: 'New Germany',
|
||||
type: 'City',
|
||||
typeID: '6fd8f29a-3d0a-32fc-980d-ea697b69da78',
|
||||
},
|
||||
{
|
||||
name: 'Brakel',
|
||||
type: 'City',
|
||||
typeID: '6fd8f29a-3d0a-32fc-980d-ea697b69da78',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
BIN
test/snapshots/base-schema.js.snap
Normal file
BIN
test/snapshots/base-schema.js.snap
Normal file
Binary file not shown.
81
test/snapshots/extended-schema.js.md
Normal file
81
test/snapshots/extended-schema.js.md
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# Snapshot report for `test/extended-schema.js`
|
||||
|
||||
The actual snapshot is saved in `extended-schema.js.snap`.
|
||||
|
||||
Generated by [AVA](https://ava.li).
|
||||
|
||||
## areas have a type and typeID
|
||||
|
||||
> Snapshot 1
|
||||
|
||||
{
|
||||
search: {
|
||||
areas: {
|
||||
nodes: [
|
||||
{
|
||||
name: 'Germany',
|
||||
type: 'Country',
|
||||
typeID: '06dd0ae4-8c74-30bb-b43d-95dcedf961de',
|
||||
},
|
||||
{
|
||||
name: 'East Germany',
|
||||
type: 'Country',
|
||||
typeID: '06dd0ae4-8c74-30bb-b43d-95dcedf961de',
|
||||
},
|
||||
{
|
||||
name: 'New Germany',
|
||||
type: 'City',
|
||||
typeID: '6fd8f29a-3d0a-32fc-980d-ea697b69da78',
|
||||
},
|
||||
{
|
||||
name: 'New Germany',
|
||||
type: 'City',
|
||||
typeID: '6fd8f29a-3d0a-32fc-980d-ea697b69da78',
|
||||
},
|
||||
{
|
||||
name: 'Brakel',
|
||||
type: 'City',
|
||||
typeID: '6fd8f29a-3d0a-32fc-980d-ea697b69da78',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
## extendedSchema: areas have a type and typeID
|
||||
|
||||
> Snapshot 1
|
||||
|
||||
{
|
||||
search: {
|
||||
areas: {
|
||||
nodes: [
|
||||
{
|
||||
name: 'Germany',
|
||||
type: 'Country',
|
||||
typeID: '06dd0ae4-8c74-30bb-b43d-95dcedf961de',
|
||||
},
|
||||
{
|
||||
name: 'East Germany',
|
||||
type: 'Country',
|
||||
typeID: '06dd0ae4-8c74-30bb-b43d-95dcedf961de',
|
||||
},
|
||||
{
|
||||
name: 'New Germany',
|
||||
type: 'City',
|
||||
typeID: '6fd8f29a-3d0a-32fc-980d-ea697b69da78',
|
||||
},
|
||||
{
|
||||
name: 'New Germany',
|
||||
type: 'City',
|
||||
typeID: '6fd8f29a-3d0a-32fc-980d-ea697b69da78',
|
||||
},
|
||||
{
|
||||
name: 'Brakel',
|
||||
type: 'City',
|
||||
typeID: '6fd8f29a-3d0a-32fc-980d-ea697b69da78',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
BIN
test/snapshots/extended-schema.js.snap
Normal file
BIN
test/snapshots/extended-schema.js.snap
Normal file
Binary file not shown.
|
|
@ -1,43 +0,0 @@
|
|||
# Snapshot report for `test/schema.js`
|
||||
|
||||
The actual snapshot is saved in `schema.js.snap`.
|
||||
|
||||
Generated by [AVA](https://ava.li).
|
||||
|
||||
## areas have a type and typeID
|
||||
|
||||
> Snapshot 1
|
||||
|
||||
{
|
||||
search: {
|
||||
areas: {
|
||||
nodes: [
|
||||
{
|
||||
name: 'Germany',
|
||||
type: 'Country',
|
||||
typeID: '06dd0ae4-8c74-30bb-b43d-95dcedf961de',
|
||||
},
|
||||
{
|
||||
name: 'East Germany',
|
||||
type: 'Country',
|
||||
typeID: '06dd0ae4-8c74-30bb-b43d-95dcedf961de',
|
||||
},
|
||||
{
|
||||
name: 'New Germany',
|
||||
type: 'City',
|
||||
typeID: '6fd8f29a-3d0a-32fc-980d-ea697b69da78',
|
||||
},
|
||||
{
|
||||
name: 'New Germany',
|
||||
type: 'City',
|
||||
typeID: '6fd8f29a-3d0a-32fc-980d-ea697b69da78',
|
||||
},
|
||||
{
|
||||
name: 'Brakel',
|
||||
type: 'City',
|
||||
typeID: '6fd8f29a-3d0a-32fc-980d-ea697b69da78',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
Binary file not shown.
|
|
@ -1,11 +1,15 @@
|
|||
import test from 'ava'
|
||||
import Node from '../../src/types/node'
|
||||
import ReleaseGroup from '../../src/types/release-group'
|
||||
import schema from '../../src/schema'
|
||||
|
||||
test('loads types from their module', t => {
|
||||
t.is(Node.resolveType({ _type: 'release-group' }), ReleaseGroup)
|
||||
t.is(
|
||||
Node.resolveType({ _type: 'release-group' }, {}, { schema }),
|
||||
ReleaseGroup
|
||||
)
|
||||
})
|
||||
|
||||
test('returns null for unknown types', t => {
|
||||
t.is(Node.resolveType({ _type: 'foo' }), null)
|
||||
test('returns undefined for unknown types', t => {
|
||||
t.is(Node.resolveType({ _type: 'foo' }, {}, { schema }), undefined)
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue