This commit is contained in:
Brian Beck 2016-09-01 01:39:27 -07:00
parent ed5339f2e1
commit 116775eaca
5 changed files with 180 additions and 270 deletions

View file

@ -1,4 +1,6 @@
import { GraphQLObjectType, GraphQLInt } from 'graphql'
import { GraphQLObjectType } from 'graphql'
import { forwardConnectionArgs } from 'graphql-relay'
import { browseResolver } from '../resolvers'
import {
MBID,
URLString,
@ -12,7 +14,17 @@ import {
URLConnection,
WorkConnection
} from '../types'
import { browseResolver } from '../resolvers'
function browseQuery (connectionType, args) {
return {
type: connectionType,
args: {
...forwardConnectionArgs,
...args
},
resolve: browseResolver()
}
}
export default new GraphQLObjectType({
name: 'BrowseQuery',
@ -20,101 +32,47 @@ export default new GraphQLObjectType({
'Browse requests are a direct lookup of all the entities directly linked ' +
'to another entity.',
fields: {
artists: {
type: ArtistConnection,
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt },
area: { type: MBID },
recording: { type: MBID },
release: { type: MBID },
releaseGroup: { type: MBID },
work: { type: MBID }
},
resolve: browseResolver()
},
events: {
type: EventConnection,
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt },
area: { type: MBID },
artist: { type: MBID },
place: { type: MBID }
},
resolve: browseResolver()
},
labels: {
type: LabelConnection,
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt },
area: { type: MBID },
release: { type: MBID }
},
resolve: browseResolver()
},
places: {
type: PlaceConnection,
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt },
area: { type: MBID }
},
resolve: browseResolver()
},
recordings: {
type: RecordingConnection,
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt },
artist: { type: MBID },
release: { type: MBID }
},
resolve: browseResolver()
},
releases: {
type: ReleaseConnection,
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt },
area: { type: MBID },
artist: { type: MBID },
label: { type: MBID },
track: { type: MBID },
trackArtist: { type: MBID },
recording: { type: MBID },
releaseGroup: { type: MBID }
},
resolve: browseResolver()
},
releaseGroups: {
type: ReleaseGroupConnection,
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt },
artist: { type: MBID },
release: { type: MBID }
},
resolve: browseResolver()
},
works: {
type: WorkConnection,
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt },
artist: { type: MBID }
},
resolve: browseResolver()
},
urls: {
type: URLConnection,
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt },
resource: { type: URLString }
},
resolve: browseResolver()
}
artists: browseQuery(ArtistConnection, {
area: { type: MBID },
recording: { type: MBID },
release: { type: MBID },
releaseGroup: { type: MBID },
work: { type: MBID }
}),
events: browseQuery(EventConnection, {
area: { type: MBID },
artist: { type: MBID },
place: { type: MBID }
}),
labels: browseQuery(LabelConnection, {
area: { type: MBID },
release: { type: MBID }
}),
places: browseQuery(PlaceConnection, {
area: { type: MBID }
}),
recordings: browseQuery(RecordingConnection, {
artist: { type: MBID },
release: { type: MBID }
}),
releases: browseQuery(ReleaseConnection, {
area: { type: MBID },
artist: { type: MBID },
label: { type: MBID },
track: { type: MBID },
trackArtist: { type: MBID },
recording: { type: MBID },
releaseGroup: { type: MBID }
}),
releaseGroups: browseQuery(ReleaseGroupConnection, {
artist: { type: MBID },
release: { type: MBID }
}),
works: browseQuery(WorkConnection, {
artist: { type: MBID }
}),
urls: browseQuery(URLConnection, {
resource: { type: URLString }
})
}
})

View file

@ -1,4 +1,6 @@
import { GraphQLObjectType } from 'graphql'
import { lookupResolver } from '../resolvers'
import { mbid } from '../types/helpers'
import {
Area,
Artist,
@ -9,10 +11,19 @@ import {
Recording,
Release,
ReleaseGroup,
Series,
URL,
Work
} from '../types'
import { lookupQuery } from '../types/helpers'
function lookupQuery (entity) {
return {
type: entity,
description: `Look up a specific ${entity.name} by its MBID.`,
args: { mbid },
resolve: lookupResolver()
}
}
export default new GraphQLObjectType({
name: 'LookupQuery',
@ -29,6 +40,7 @@ export default new GraphQLObjectType({
recording: lookupQuery(Recording),
release: lookupQuery(Release),
releaseGroup: lookupQuery(ReleaseGroup),
series: lookupQuery(Series),
url: lookupQuery(URL),
work: lookupQuery(Work)
}

View file

@ -1,4 +1,6 @@
import { GraphQLObjectType } from 'graphql'
import { GraphQLObjectType, GraphQLNonNull, GraphQLString } from 'graphql'
import { forwardConnectionArgs } from 'graphql-relay'
import { searchResolver } from '../resolvers'
import {
AreaConnection,
ArtistConnection,
@ -9,7 +11,17 @@ import {
ReleaseGroupConnection,
WorkConnection
} from '../types'
import { searchQuery } from '../types/helpers'
function searchQuery (connectionType) {
return {
type: connectionType,
args: {
query: { type: new GraphQLNonNull(GraphQLString) },
...forwardConnectionArgs
},
resolve: searchResolver()
}
}
export default new GraphQLObjectType({
name: 'SearchQuery',

View file

@ -1,5 +1,9 @@
import { toEntityType } from './types/helpers'
import { getOffsetWithDefault, connectionFromArraySlice } from 'graphql-relay'
import {
getOffsetWithDefault,
connectionFromArray,
connectionFromArraySlice
} from 'graphql-relay'
import { getFields, extendIncludes } from './util'
export function includeRelations (params, info) {
@ -35,34 +39,63 @@ export function includeSubqueries (params, info) {
return params
}
export function lookupResolver (entityType, extraParams = {}) {
return (root, { id }, { lookupLoader }, info) => {
const params = includeRelations(extraParams, info)
entityType = entityType || toEntityType(info.fieldName)
return lookupLoader.load([entityType, id, params])
export function lookupResolver () {
return (root, { mbid }, { lookupLoader }, info) => {
const entityType = toEntityType(info.fieldName)
const params = includeRelations({}, info)
return lookupLoader.load([entityType, mbid, params])
}
}
export function browseResolver () {
return (source, args, { browseLoader }, info) => {
return (source, { first = 25, after, ...args }, { browseLoader }, info) => {
const pluralName = toEntityType(info.fieldName)
let singularName = pluralName
if (pluralName.endsWith('s')) {
singularName = pluralName.slice(0, -1)
}
const params = args
return browseLoader.load([singularName, params])
const { type, types, status, statuses, ...moreParams } = args
let params = {
...moreParams,
type: [],
status: [],
limit: first,
offset: getOffsetWithDefault(after, 0)
}
params = includeSubqueries(params, info)
params = includeRelations(params, info)
if (type) {
params.type.push(type)
}
if (types) {
params.type.push(...types)
}
if (status) {
params.status.push(status)
}
if (statuses) {
params.status.push(...statuses)
}
return browseLoader.load([singularName, params]).then(list => {
const {
[pluralName]: arraySlice,
[`${singularName}-offset`]: sliceStart,
[`${singularName}-count`]: arrayLength
} = list
const meta = { sliceStart, arrayLength }
return connectionFromArraySlice(arraySlice, { first, after }, meta)
})
}
}
export function searchResolver () {
return (source, args, { searchLoader }, info) => {
return (source, { first = 25, after, ...args }, { searchLoader }, info) => {
const pluralName = toEntityType(info.fieldName)
let singularName = pluralName
if (pluralName.endsWith('s')) {
singularName = pluralName.slice(0, -1)
}
const { query, first, after, ...params } = args
const { query, ...params } = args
params.limit = first
params.offset = getOffsetWithDefault(after, 0)
return searchLoader.load([singularName, query, params]).then(list => {
@ -78,61 +111,31 @@ export function searchResolver () {
}
export function relationResolver () {
return (source, { offset = 0,
limit,
direction,
type,
typeID }, { lookupLoader }, info) => {
return (source, args, context, info) => {
const targetType = toEntityType(info.fieldName).replace('-', '_')
return source.filter(relation => {
const relations = source.filter(relation => {
if (relation['target-type'] !== targetType) {
return false
}
if (direction != null && relation.direction !== direction) {
if (args.direction != null && relation.direction !== args.direction) {
return false
}
if (type != null && relation.type !== type) {
if (args.type != null && relation.type !== args.type) {
return false
}
if (typeID != null && relation['type-id'] !== typeID) {
if (args.typeID != null && relation['type-id'] !== args.typeID) {
return false
}
return true
}).slice(offset, limit == null ? undefined : offset + limit)
})
return connectionFromArray(relations, args)
}
}
export function linkedResolver () {
return (source, args, { browseLoader }, info) => {
const pluralName = toEntityType(info.fieldName)
let singularName = pluralName
if (pluralName.endsWith('s')) {
singularName = pluralName.slice(0, -1)
}
return (source, args, context, info) => {
const parentEntity = toEntityType(info.parentType.name)
let params = {
[parentEntity]: source.id,
type: [],
status: [],
limit: args.limit,
offset: args.offset
}
params = includeSubqueries(params, info)
params = includeRelations(params, info)
if (args.type) {
params.type.push(args.type)
}
if (args.types) {
params.type.push(...args.types)
}
if (args.status) {
params.status.push(args.status)
}
if (args.statuses) {
params.status.push(...args.statuses)
}
return browseLoader.load([singularName, params]).then(list => {
return list[pluralName]
})
args = { ...args, [parentEntity]: source.id }
return browseResolver()(source, args, context, info)
}
}

View file

@ -3,44 +3,36 @@ import pascalCase from 'pascalcase'
import {
GraphQLObjectType,
GraphQLString,
GraphQLInt,
GraphQLList,
GraphQLNonNull
} from 'graphql'
import { globalIdField, forwardConnectionArgs } from 'graphql-relay'
import {
globalIdField,
connectionArgs,
forwardConnectionArgs
} from 'graphql-relay'
import { MBID } from './scalars'
import { ReleaseGroupType, ReleaseStatus } from './enums'
import ArtistCredit from './artist-credit'
import Artist from './artist'
import Event from './event'
import Label from './label'
import { ArtistConnection } from './artist'
import { EventConnection } from './event'
import { LabelConnection } from './label'
import LifeSpan from './life-span'
import Place from './place'
import Recording from './recording'
import { PlaceConnection } from './place'
import { RecordingConnection } from './recording'
import Relation from './relation'
import Release from './release'
import ReleaseGroup from './release-group'
import Work from './work'
import { ReleaseConnection } from './release'
import { ReleaseGroupConnection } from './release-group'
import { WorkConnection } from './work'
import {
lookupResolver,
linkedResolver,
relationResolver,
searchResolver,
includeRelations
} from '../resolvers'
export const toNodeType = pascalCase
export const toEntityType = dashify
export function getByline (data) {
const credit = data['artist-credit']
if (credit && credit.length) {
return credit.reduce((byline, credit) => {
return byline + credit.name + credit.joinphrase
}, '')
}
}
export function fieldWithID (name, config = {}) {
config = {
type: GraphQLString,
@ -76,29 +68,10 @@ export function getFallback (keys) {
}
}
export function lookupQuery (entity, params) {
return {
type: entity,
description: `Look up a specific ${entity.name} by its MBID.`,
args: { id },
resolve: lookupResolver(dashify(entity.name), params)
}
}
export function searchQuery (connectionType) {
return {
type: connectionType,
args: {
query: { type: new GraphQLNonNull(GraphQLString) },
...forwardConnectionArgs
},
resolve: searchResolver()
}
}
export const id = globalIdField()
export const mbid = {
type: new GraphQLNonNull(MBID),
description: 'The MBID of the entity.',
resolve: source => source.id
}
export const name = { type: GraphQLString }
@ -107,11 +80,21 @@ export const title = { type: GraphQLString }
export const disambiguation = { type: GraphQLString }
export const lifeSpan = { type: LifeSpan, resolve: getHyphenated }
function linkedQuery (connectionType, args) {
return {
type: connectionType,
args: {
...forwardConnectionArgs,
...args
},
resolve: linkedResolver()
}
}
export const relation = {
type: new GraphQLList(Relation),
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt },
...connectionArgs,
direction: { type: GraphQLString },
type: { type: GraphQLString },
typeID: { type: MBID }
@ -141,7 +124,7 @@ export const relations = {
if (source.relations != null) {
return source.relations
}
const entityType = dashify(info.parentType.name)
const entityType = toEntityType(info.parentType.name)
const id = source.id
const params = includeRelations({}, info)
return lookupLoader.load([entityType, id, params]).then(entity => {
@ -166,80 +149,22 @@ export const artistCredit = {
}
}
export const artists = {
type: new GraphQLList(Artist),
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt }
},
resolve: linkedResolver()
}
export const artists = linkedQuery(ArtistConnection)
export const events = linkedQuery(EventConnection)
export const labels = linkedQuery(LabelConnection)
export const places = linkedQuery(PlaceConnection)
export const recordings = linkedQuery(RecordingConnection)
export const events = {
type: new GraphQLList(Event),
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt }
},
resolve: linkedResolver()
}
export const releases = linkedQuery(ReleaseConnection, {
type: { type: ReleaseGroupType },
types: { type: new GraphQLList(ReleaseGroupType) },
status: { type: ReleaseStatus },
statuses: { type: new GraphQLList(ReleaseStatus) }
})
export const labels = {
type: new GraphQLList(Label),
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt }
},
resolve: linkedResolver()
}
export const releaseGroups = linkedQuery(ReleaseGroupConnection, {
type: { type: ReleaseGroupType },
types: { type: new GraphQLList(ReleaseGroupType) }
})
export const places = {
type: new GraphQLList(Place),
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt }
},
resolve: linkedResolver()
}
export const recordings = {
type: new GraphQLList(Recording),
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt }
},
resolve: linkedResolver()
}
export const releases = {
type: new GraphQLList(Release),
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt },
type: { type: ReleaseGroupType },
types: { type: new GraphQLList(ReleaseGroupType) },
status: { type: ReleaseStatus },
statuses: { type: new GraphQLList(ReleaseStatus) }
},
resolve: linkedResolver()
}
export const releaseGroups = {
type: new GraphQLList(ReleaseGroup),
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt },
type: { type: ReleaseGroupType },
types: { type: new GraphQLList(ReleaseGroupType) }
},
resolve: linkedResolver()
}
export const works = {
type: new GraphQLList(Work),
args: {
limit: { type: GraphQLInt },
offset: { type: GraphQLInt }
},
resolve: linkedResolver()
}
export const works = linkedQuery(WorkConnection)