@ssb-graphql/whakapapa
Version:
GraphQL types and resolvers for the ssb-whakapapa plugin
246 lines (219 loc) • 8.21 kB
JavaScript
const uniqby = require('lodash.uniqby')
const pick = require('lodash.pick')
const CHILD_LINK = require('ssb-whakapapa/spec/link/profile-profile/child').type
const PARTNER_LINK = require('ssb-whakapapa/spec/link/profile-profile/partner').type
const { promisify } = require('util')
const ssbResolvers = require('./ssb')
const fixInput = require('./fix-input')
const findTreeLinks = require('./find-tree-links')
module.exports = function Resolvers (ssb, externalGetters) {
const { getArtefact, getProfile, getStory } = externalGetters
const {
getLinks,
getWhakapapaLink,
loadFamilyOfPerson,
gettersWithCache,
tombstoneProfileAndLinks,
gettersWithCache: {
getChildLink,
getPartnerLink
}
} = ssbResolvers(ssb, externalGetters)
const resolvers = {
Query: {
whakapapaView: (_, { id }) => ssb.whakapapa.view.get(id),
whakapapaViews: (_, { groupId }) => ssb.whakapapa.view.list({
groupId,
descending: false // = ascending timestamps = oldest first
}),
async getDescendantLinks (_, { profileId }) {
const msgVal = await promisify(ssb.get)({ id: profileId, private: true })
const groupId = msgVal.content.recps && msgVal.content.recps[0]
const childLinks = await ssb.whakapapa.child.list({
groupId,
read: getChildLink // cached
})
const partnerLinks = await ssb.whakapapa.partner.list({
groupId,
read: getPartnerLink // cached
})
// build a graph from the focus down
return findTreeLinks(profileId, childLinks, partnerLinks)
},
whakapapaLink (_, { parent, child, isPartner }) {
return getWhakapapaLink({ parent, child, type: isPartner ? PARTNER_LINK : CHILD_LINK })
},
loadFamilyOfPerson (_, { id, extended }) {
return loadFamilyOfPerson(id, { extended })
}
},
Mutation: {
saveWhakapapaView: (_, { input }) => {
const details = fixInput.whakapapaView(input)
if (!input.id) return ssb.whakapapa.view.create(details)
return ssb.whakapapa.view.update(input.id, details)
},
saveLink: (_, { input }) => {
const details = fixInput.link(input)
if (!input.linkId) {
return ssb.whakapapa.link.create(
pick(input, ['type', 'parent', 'child']),
details
)
}
return ssb.whakapapa.link.update(input.linkId, details)
.then(updateId => input.linkId) // return the linkId not updateId
},
tombstoneProfileAndLinks: (_, opts) => {
const { id, details = {}, allowPublic } = opts
if (allowPublic) details.allowPublic = true
return tombstoneProfileAndLinks(id, details)
}
},
// resolvers for nested details
WhakapapaView: {
id: (whakapapaView) => whakapapaView.key,
importantRelationships: (whakapapaView) => {
return Object.entries(whakapapaView.importantRelationships)
.filter(([_, important]) => important.length > 0)
.map(([mainProfileId, important]) => {
return {
profileId: mainProfileId,
primary: { mainProfileId, profileId: important[0] },
other: important.slice(1, important.length)
.map(profileId => ({ mainProfileId, profileId }))
}
})
},
links: async (whakapapaView) => {
const { recps } = whakapapaView
// IDEA
// if (recordCount < 1000) do an iterative lookup
// else do a bulk lookup
const childLinks = await ssb.whakapapa.child.list({
groupId: recps[0],
read: getChildLink // cached
})
const partnerLinks = await ssb.whakapapa.partner.list({
groupId: recps[0],
read: getPartnerLink // cached
})
// build a graph from the focus down
return findTreeLinks(whakapapaView.focus, childLinks, partnerLinks)
}
},
Relationship: {
relationshipType: async ({ mainProfileId, profileId }) => {
// find the relationship between the mainProfileId and profileId
let link = await getWhakapapaLink({ type: CHILD_LINK, parent: profileId, child: mainProfileId, isDirectLink: false })
if (link) return link.relationshipType
// if no link was found, search to see if its a partner link instead
link = await getWhakapapaLink({ type: PARTNER_LINK, parent: mainProfileId, child: profileId })
if (link) return 'partner'
return null
}
},
Person: {
relationshipType: profile => profile.relationshipType || null,
legallyAdopted: profile => profile.legallyAdopted || null,
parents: async profile => {
const parentLinks = await getParentLinks(profile)
return parentLinks.map(link => {
return {
...link.profile,
relationshipType: link.relationshipType,
legallyAdopted: link.legallyAdopted
}
})
},
children: async profile => {
const childLinks = await getChildLinks(profile)
return childLinks
.sort((a, b) => a.profile.birthOrder - b.profile.birthOrder)
.map(link => {
return {
...link.profile,
relationshipType: link.relationshipType,
legallyAdopted: link.legallyAdopted
}
})
},
partners: async profile => {
const partnerLinks = await getLinks({
type: PARTNER_LINK,
parentId: profile.id,
childId: profile.id,
getter: getProfile
})
return partnerLinks.map(link => link.profile)
},
siblings: async profile => {
// get all of this profiles parents
const parentLinks = await getParentLinks(profile)
// get the links
const childLinks = await Promise.all(
parentLinks.map(link => getChildLinks(link)) // this works because link.id is the profile we want
)
const links = childLinks
.reduce((acc, childParentLinks) => {
return [...acc, ...childParentLinks.map(link => link.profile)]
}, [])
.filter(p => p.id !== profile.id)
return uniqby(links, 'id')
},
contributorLinks: profile => getLinks({ type: 'link/story-profile/contributor', childId: profile.id, getter: getStory }),
mentionLinks: profile => getLinks({ type: 'link/story-profile/mention', childId: profile.id, getter: getStory }),
creatorLinks: profile => getLinks({ type: 'link/story-profile/creator', childId: profile.id, getter: getStory })
},
Story: {
artefactLinks: story => getLinks({ type: 'link/story-artefact', parentId: story.id, getter: getArtefact }),
mentionLinks: story => getLinks({ type: 'link/story-profile/mention', parentId: story.id, getter: getProfile }),
contributorLinks: story => getLinks({ type: 'link/story-profile/contributor', parentId: story.id, getter: getProfile }),
creatorLinks: story => getLinks({ type: 'link/story-profile/creator', parentId: story.id, getter: getProfile }),
storyLinks: story => getLinks({ type: 'link/story-story', parentId: story.id, childId: story.id, getter: getStory })
},
Artefact: {
storyLinks: getArtefactStoryLinks
},
Photo: {
storyLinks: getArtefactStoryLinks
},
Video: {
storyLinks: getArtefactStoryLinks
},
Audio: {
storyLinks: getArtefactStoryLinks
},
Document: {
storyLinks: getArtefactStoryLinks
}
}
function getArtefactStoryLinks (artefact) {
return getLinks({
type: 'link/story-artefact',
childId: artefact.id,
// parentId = ?
getter: getStory
})
}
function getChildLinks (profile) {
return getLinks({
type: CHILD_LINK,
// childId = ?
parentId: profile.id,
getter: getProfile
})
}
function getParentLinks (profile) {
return getLinks({
type: CHILD_LINK,
childId: profile.id,
// parentId = ?
getter: getProfile
})
}
return {
resolvers,
gettersWithCache
}
}