@ssb-graphql/whakapapa
Version:
GraphQL types and resolvers for the ssb-whakapapa plugin
102 lines (84 loc) • 3.29 kB
JavaScript
const pull = require('pull-stream')
const paraMap = require('pull-paramap')
const { promisify: p } = require('util')
module.exports = function GetLinks (sbot, cache) {
return function getLinks ({ type, parentId, childId, getter }, cb) {
if (cb === undefined) return p(getLinks)({ type, parentId, childId, getter })
if (!type) return cb(new Error(`get-links expects a type, got ${type}`))
if ((!parentId && !childId)) {
return cb(new Error('get-links expects either a parentId, childId or both'))
} else if (parentId && childId && (parentId !== childId)) {
return cb(new Error('get-links expects parentId and childId to be equal when they are both given'))
}
if (getter && typeof getter !== 'function') return cb(new Error('the getter provided was not a function'))
readLinks(type, parentId, childId, (err, links) => {
if (err) return cb(err)
pull(
pull.values(links),
pull.filter(link => link.tombstone === null),
pull.unique(link => link.id),
getterMap(getter),
pull.collect((err, links) => {
if (err) return cb(err)
cb(null, links)
})
)
})
function getterMap (getter) {
if (!getter) return null
const outputIdToLink = {}
// we assume that if parentId is included we want to find out about its children
// and if childId is included, we want to know about the parents
const types = getTypes(type)
const outputType = parentId ? types.child : types.parent
return pull(
pull.map(link => {
outputIdToLink[link.id] = link // keep a copy of this link
return link.id // map links to id
}),
paraMap((key, cb) => {
// use the getter to fetch the full object of the type (e.g. story, profile, artefact, etc...)
getter(key, (err, output) => {
if (err) {
console.error(`Unable to get record ${key}, error:`)
console.error(err)
return cb(null, null)
// HACK : don't kill all record getting if just one fails
}
cb(null, output)
})
}, 4),
pull.filter(Boolean), // filter out null values
pull.filter(output => output.tombstone === null), // filter out tombstoned items
pull.map(output => ({
...outputIdToLink[output.id],
linkId: outputIdToLink[output.id].linkId,
[outputType]: output
}))
)
}
}
function readLinks (type, parent, child, cb) {
const cached = cache.get(type, { parent, child })
if (cached) return cb(null, cached)
// use either for the id
const id = parent || child
sbot.whakapapa.link.getLinksOfType(id, type, (err, links) => {
if (err) return cb(err)
if (parent) cache.setParent(type, parent, links.childLinks)
if (child) cache.setChild(type, child, links.parentLinks)
cb(null, cache.get(type, { parent, child }))
})
}
}
function getTypes (type) {
const types = type
.replace(/^link\//, '')
.replace(/\/.*$/, '')
.split('-')
if (types.length !== 2) throw new Error(`tried to resolve parent/child of ${type} but failed!`)
return {
parent: types[0],
child: types[1]
}
}