mirror of
https://github.com/BradNut/graphbrainz
synced 2025-09-08 17:40:32 +00:00
243 lines
7.1 KiB
JavaScript
243 lines
7.1 KiB
JavaScript
import { toDashed, toSingular } from './types/helpers'
|
|
import {
|
|
getOffsetWithDefault,
|
|
connectionFromArray,
|
|
connectionFromArraySlice
|
|
} from 'graphql-relay'
|
|
import { getFields, extendIncludes } from './util'
|
|
|
|
export function includeRelationships(params, info, fragments = info.fragments) {
|
|
let fields = getFields(info, fragments)
|
|
if (info.fieldName !== 'relationships') {
|
|
if (fields.relationships) {
|
|
fields = getFields(fields.relationships, fragments)
|
|
} else {
|
|
if (fields.edges) {
|
|
fields = getFields(fields.edges, fragments)
|
|
if (fields.node) {
|
|
return includeRelationships(params, fields.node, fragments)
|
|
}
|
|
}
|
|
return params
|
|
}
|
|
}
|
|
if (fields) {
|
|
const relationships = Object.keys(fields)
|
|
const includeRels = relationships.map(field => {
|
|
return `${toDashed(toSingular(field))}-rels`
|
|
})
|
|
if (includeRels.length) {
|
|
params = {
|
|
...params,
|
|
inc: extendIncludes(params.inc, includeRels)
|
|
}
|
|
}
|
|
}
|
|
return params
|
|
}
|
|
|
|
export function includeSubqueries(params, info, fragments = info.fragments) {
|
|
const subqueryIncludes = {
|
|
aliases: ['aliases'],
|
|
artistCredit: ['artist-credits'],
|
|
artistCredits: ['artist-credits'],
|
|
isrcs: ['isrcs'],
|
|
media: ['media', 'discids'],
|
|
rating: ['ratings'],
|
|
tags: ['tags']
|
|
}
|
|
let fields = getFields(info, fragments)
|
|
const include = []
|
|
for (const key in subqueryIncludes) {
|
|
if (fields[key]) {
|
|
const value = subqueryIncludes[key]
|
|
include.push(...value)
|
|
}
|
|
}
|
|
params = {
|
|
...params,
|
|
inc: extendIncludes(params.inc, include)
|
|
}
|
|
if (fields.edges) {
|
|
fields = getFields(fields.edges, fragments)
|
|
if (fields.node) {
|
|
params = includeSubqueries(params, fields.node, fragments)
|
|
}
|
|
}
|
|
return params
|
|
}
|
|
|
|
export function resolveLookup(root, { mbid, ...params }, { loaders }, info) {
|
|
if (!mbid && !params.resource) {
|
|
throw new Error('Lookups by a field other than MBID must provide: resource')
|
|
}
|
|
const entityType = toDashed(info.fieldName)
|
|
params = includeSubqueries(params, info)
|
|
params = includeRelationships(params, info)
|
|
return loaders.lookup.load([entityType, mbid, params])
|
|
}
|
|
|
|
export function resolveBrowse(
|
|
root,
|
|
{ first, after, type = [], status = [], discID, isrc, iswc, ...args },
|
|
{ loaders },
|
|
info
|
|
) {
|
|
const pluralName = toDashed(info.fieldName)
|
|
const singularName = toSingular(pluralName)
|
|
let params = {
|
|
...args,
|
|
type,
|
|
status,
|
|
limit: first,
|
|
offset: getOffsetWithDefault(after, -1) + 1 || undefined
|
|
}
|
|
params = includeSubqueries(params, info)
|
|
params = includeRelationships(params, info, info.fragments)
|
|
const formatParam = value => value.toLowerCase().replace(/ /g, '')
|
|
params.type = params.type.map(formatParam)
|
|
params.status = params.status.map(formatParam)
|
|
let request
|
|
if (discID) {
|
|
request = loaders.lookup.load(['discid', discID, params])
|
|
// If fetching releases by disc ID, they will already include the `media`
|
|
// and `discids` subqueries, and it is invalid to specify them.
|
|
if (params.inc) {
|
|
params.inc = params.inc.filter(value => {
|
|
return value !== 'media' && value !== 'discids'
|
|
})
|
|
}
|
|
} else if (isrc) {
|
|
request = loaders.lookup.load(['isrc', isrc, params])
|
|
} else if (iswc) {
|
|
request = loaders.lookup.load(['iswc', iswc, params])
|
|
} else {
|
|
request = loaders.browse.load([singularName, params])
|
|
}
|
|
return request.then(list => {
|
|
// Grab the list, offet, and count from the response and use them to build
|
|
// a Relay connection object.
|
|
const {
|
|
[pluralName]: arraySlice,
|
|
[`${singularName}-offset`]: sliceStart = 0,
|
|
[`${singularName}-count`]: arrayLength = arraySlice.length
|
|
} = list
|
|
const meta = { sliceStart, arrayLength }
|
|
const connection = connectionFromArraySlice(
|
|
arraySlice,
|
|
{ first, after },
|
|
meta
|
|
)
|
|
return {
|
|
nodes: connection.edges.map(edge => edge.node),
|
|
totalCount: arrayLength,
|
|
...connection
|
|
}
|
|
})
|
|
}
|
|
|
|
export function resolveSearch(
|
|
root,
|
|
{ after, first, query, ...args },
|
|
{ loaders },
|
|
info
|
|
) {
|
|
const pluralName = toDashed(info.fieldName)
|
|
const singularName = toSingular(pluralName)
|
|
let params = {
|
|
...args,
|
|
limit: first,
|
|
offset: getOffsetWithDefault(after, -1) + 1 || undefined
|
|
}
|
|
params = includeSubqueries(params, info)
|
|
return loaders.search.load([singularName, query, params]).then(list => {
|
|
const {
|
|
[pluralName]: arraySlice,
|
|
offset: sliceStart,
|
|
count: arrayLength
|
|
} = list
|
|
const meta = { sliceStart, arrayLength }
|
|
const connection = connectionFromArraySlice(
|
|
arraySlice,
|
|
{ first, after },
|
|
meta
|
|
)
|
|
// Move the `score` field up to the edge object and make sure it's a
|
|
// number (MusicBrainz returns a string).
|
|
const edges = connection.edges.map(edge => ({
|
|
...edge,
|
|
score: +edge.node.score
|
|
}))
|
|
const connectionWithExtras = {
|
|
nodes: edges.map(edge => edge.node),
|
|
totalCount: arrayLength,
|
|
...connection,
|
|
edges
|
|
}
|
|
return connectionWithExtras
|
|
})
|
|
}
|
|
|
|
export function resolveRelationship(rels, args, context, info) {
|
|
const targetType = toDashed(toSingular(info.fieldName)).replace('-', '_')
|
|
let matches = rels.filter(rel => rel['target-type'] === targetType)
|
|
// There's no way to filter these at the API level, so do it here.
|
|
if (args.direction != null) {
|
|
matches = matches.filter(rel => rel.direction === args.direction)
|
|
}
|
|
if (args.type != null) {
|
|
matches = matches.filter(rel => rel.type === args.type)
|
|
}
|
|
if (args.typeID != null) {
|
|
matches = matches.filter(rel => rel['type-id'] === args.typeID)
|
|
}
|
|
const connection = connectionFromArray(matches, args)
|
|
return {
|
|
nodes: connection.edges.map(edge => edge.node),
|
|
totalCount: matches.length,
|
|
...connection
|
|
}
|
|
}
|
|
|
|
export function resolveLinked(entity, args, context, info) {
|
|
const parentEntity = toDashed(info.parentType.name)
|
|
args = { ...args, [parentEntity]: entity.id }
|
|
return resolveBrowse(entity, args, context, info)
|
|
}
|
|
|
|
/**
|
|
* If we weren't smart enough or weren't able to include the `inc` parameter
|
|
* for a particular field that's being requested, make another request to grab
|
|
* it (after making sure it isn't already available).
|
|
*/
|
|
export function createSubqueryResolver(
|
|
{ inc, key } = {},
|
|
handler = value => value
|
|
) {
|
|
return (entity, args, { loaders }, info) => {
|
|
key = key || toDashed(info.fieldName)
|
|
let promise
|
|
if (key in entity) {
|
|
promise = Promise.resolve(entity)
|
|
} else {
|
|
const { _type: entityType, id } = entity
|
|
const params = { inc: [inc || key] }
|
|
promise = loaders.lookup.load([entityType, id, params])
|
|
}
|
|
return promise.then(entity => handler(entity[key], args))
|
|
}
|
|
}
|
|
|
|
export function resolveDiscReleases(disc, args, context, info) {
|
|
const { releases } = disc
|
|
if (releases != null) {
|
|
const connection = connectionFromArray(releases, args)
|
|
return {
|
|
nodes: connection.edges.map(edge => edge.node),
|
|
totalCount: releases.length,
|
|
...connection
|
|
}
|
|
}
|
|
args = { ...args, discID: disc.id }
|
|
return resolveBrowse(disc, args, context, info)
|
|
}
|