UNPKG

@ssb-graphql/whakapapa

Version:

GraphQL types and resolvers for the ssb-whakapapa plugin

246 lines (219 loc) 8.21 kB
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 } }