graphbrainz/src/types/helpers.js

257 lines
7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import dashify from 'dashify'
import pascalCase from 'pascalcase'
import {
GraphQLObjectType,
GraphQLString,
GraphQLInt,
GraphQLList,
GraphQLNonNull
} from 'graphql'
import {
globalIdField,
connectionArgs,
connectionDefinitions,
connectionFromArray,
forwardConnectionArgs
} from 'graphql-relay'
import { MBID } from './scalars'
import { ReleaseGroupType, ReleaseStatus } from './enums'
import Alias from './alias'
import ArtistCredit from './artist-credit'
import { ArtistConnection } from './artist'
import { EventConnection } from './event'
import { LabelConnection } from './label'
import LifeSpan from './life-span'
import { PlaceConnection } from './place'
import { RecordingConnection } from './recording'
import { RelationshipConnection } from './relationship'
import { ReleaseConnection } from './release'
import { ReleaseGroupConnection } from './release-group'
import { TagConnection } from './tag'
import { WorkConnection } from './work'
import {
resolveLinked,
resolveRelationship,
createSubqueryResolver,
includeRelationships
} from '../resolvers'
export const toPascal = pascalCase
export const toDashed = dashify
export function toPlural (name) {
return name.endsWith('s') ? name : name + 's'
}
export function toSingular (name) {
return name.endsWith('s') && !/series/i.test(name) ? name.slice(0, -1) : name
}
export function toWords (name) {
return toPascal(name).replace(/([^A-Z])?([A-Z]+)/g, (match, tail, head) => {
tail = tail ? tail + ' ' : ''
head = head.length > 1 ? head : head.toLowerCase()
return `${tail}${head}`
})
}
export function fieldWithID (name, config = {}) {
config = {
type: GraphQLString,
resolve: resolveHyphenated,
...config
}
const isPlural = config.type instanceof GraphQLList
const singularName = isPlural ? toSingular(name) : name
const idName = isPlural ? `${singularName}IDs` : `${name}ID`
const s = isPlural ? 's' : ''
const idConfig = {
type: isPlural ? new GraphQLList(MBID) : MBID,
description: `The MBID${s} associated with the value${s} of the \`${name}\`
field.`,
resolve: resolveHyphenated
}
return {
[name]: config,
[idName]: idConfig
}
}
export function resolveHyphenated (obj, args, context, info) {
const name = dashify(info.fieldName)
return obj[name]
}
export function resolveWithFallback (keys) {
return (obj) => {
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
if (key in obj) {
return obj[key]
}
}
}
}
export const id = globalIdField()
export const mbid = {
type: new GraphQLNonNull(MBID),
description: 'The MBID of the entity.',
resolve: entity => entity.id
}
export const name = {
type: GraphQLString,
description: 'The official name of the entity.'
}
export const sortName = {
type: GraphQLString,
description: `The string to use for the purpose of ordering by name (for
example, by moving articles like the to the end or a persons last name to
the front).`,
resolve: resolveHyphenated
}
export const title = {
type: GraphQLString,
description: 'The official title of the entity.'
}
export const disambiguation = {
type: GraphQLString,
description: 'A comment used to help distinguish identically named entitites.'
}
export const lifeSpan = {
type: LifeSpan,
description: `The begin and end dates of the entitys existence. Its exact
meaning depends on the type of entity.`,
resolve: resolveHyphenated
}
function linkedQuery (connectionType, { args, ...config } = {}) {
const typeName = toPlural(toWords(connectionType.name.slice(0, -10)))
return {
type: connectionType,
description: `A list of ${typeName} linked to this entity.`,
args: {
...forwardConnectionArgs,
...args
},
resolve: resolveLinked,
...config
}
}
export const relationship = {
type: RelationshipConnection,
description: 'A list of relationships between these two entity types.',
args: {
...connectionArgs,
direction: {
type: GraphQLString,
description: 'Filter by the relationship direction.'
},
...fieldWithID('type', {
description: 'Filter by the relationship type.'
})
},
resolve: resolveRelationship
}
export const relationships = {
type: new GraphQLObjectType({
name: 'Relationships',
description: 'Lists of entity relationships for each entity type.',
fields: () => ({
areas: relationship,
artists: relationship,
events: relationship,
instruments: relationship,
labels: relationship,
places: relationship,
recordings: relationship,
releases: relationship,
releaseGroups: relationship,
series: relationship,
urls: relationship,
works: relationship
})
}),
description: 'Relationships between this entity and other entitites.',
resolve: (entity, args, { loaders }, info) => {
let promise
if (entity.relations != null) {
promise = Promise.resolve(entity)
} else {
const entityType = toDashed(info.parentType.name)
const id = entity.id
const params = includeRelationships({}, info)
promise = loaders.lookup.load([entityType, id, params])
}
return promise.then(entity => entity.relations)
}
}
export const aliases = {
type: new GraphQLList(Alias),
description: `[Aliases](https://musicbrainz.org/doc/Aliases) are used to store
alternate names or misspellings.`,
resolve: createSubqueryResolver()
}
export const artistCredit = {
type: new GraphQLList(ArtistCredit),
description: 'The main credited artist(s).',
resolve: createSubqueryResolver()
}
export const releaseGroupType = {
type: new GraphQLList(ReleaseGroupType),
description: 'Filter by one or more release group types.'
}
export const releaseStatus = {
type: new GraphQLList(ReleaseStatus),
description: 'Filter by one or more release statuses.'
}
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 releases = linkedQuery(ReleaseConnection, {
args: {
type: releaseGroupType,
status: releaseStatus
}
})
export const releaseGroups = linkedQuery(ReleaseGroupConnection, {
args: {
type: releaseGroupType
}
})
export const tags = linkedQuery(TagConnection, {
resolve: createSubqueryResolver('tags', (value = [], args) => ({
totalCount: value.length,
...connectionFromArray(value, args)
}))
})
export const works = linkedQuery(WorkConnection)
export const totalCount = {
type: GraphQLInt,
description: `A count of the total number of items in this connection,
ignoring pagination.`
}
export const score = {
type: GraphQLInt,
description: `The relevancy score (0100) assigned by the search engine, if
these results were found through a search.`
}
export function connectionWithExtras (nodeType) {
return connectionDefinitions({
nodeType,
connectionFields: () => ({ totalCount }),
edgeFields: () => ({ score })
}).connectionType
}