mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Implement+test SocialMedia.ownedBy resolver
Explicitly define `SocialMedia` graphql type and exclude it from `neo4j-graphql-js`.
This commit is contained in:
parent
448706ab18
commit
c65cc3e0e5
@ -19,6 +19,7 @@ export default applyScalars(
|
||||
'Notfication',
|
||||
'Statistics',
|
||||
'LoggedInUser',
|
||||
'SocialMedia',
|
||||
],
|
||||
// add 'User' here as soon as possible
|
||||
},
|
||||
@ -30,6 +31,7 @@ export default applyScalars(
|
||||
'Notfication',
|
||||
'Statistics',
|
||||
'LoggedInUser',
|
||||
'SocialMedia',
|
||||
],
|
||||
// add 'User' here as soon as possible
|
||||
},
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import scrape from './embeds/scraper.js'
|
||||
import { undefinedToNull } from '../helpers'
|
||||
import { undefinedToNullResolver } from './helpers/Resolver'
|
||||
|
||||
export default {
|
||||
Query: {
|
||||
@ -8,7 +8,7 @@ export default {
|
||||
},
|
||||
},
|
||||
Embed: {
|
||||
...undefinedToNull([
|
||||
...undefinedToNullResolver([
|
||||
'type',
|
||||
'title',
|
||||
'author',
|
||||
|
||||
75
backend/src/schema/resolvers/helpers/Resolver.js
Normal file
75
backend/src/schema/resolvers/helpers/Resolver.js
Normal file
@ -0,0 +1,75 @@
|
||||
import { neode } from '../../../bootstrap/neo4j'
|
||||
|
||||
export const undefinedToNullResolver = list => {
|
||||
const resolvers = {}
|
||||
list.forEach(key => {
|
||||
resolvers[key] = async (parent, params, context, resolveInfo) => {
|
||||
return typeof parent[key] === 'undefined' ? null : parent[key]
|
||||
}
|
||||
})
|
||||
return resolvers
|
||||
}
|
||||
|
||||
export default function Resolver(type, options = {}) {
|
||||
const instance = neode()
|
||||
const {
|
||||
idAttribute = 'id',
|
||||
undefinedToNull = [],
|
||||
count = {},
|
||||
hasOne = {},
|
||||
hasMany = {},
|
||||
} = options
|
||||
const _hasResolver = (resolvers, { key, connection }, { returnType }) => {
|
||||
return async (parent, params, context, resolveInfo) => {
|
||||
if (typeof parent[key] !== 'undefined') return parent[key]
|
||||
const id = parent[idAttribute]
|
||||
const statement = `MATCH(:${type} {${idAttribute}: {id}})${connection} RETURN related`
|
||||
const result = await instance.cypher(statement, { id })
|
||||
let response = result.records.map(r => r.get('related').properties)
|
||||
if (returnType === 'object') response = response[0] || null
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
const countResolver = obj => {
|
||||
const resolvers = {}
|
||||
for (const [key, connection] of Object.entries(obj)) {
|
||||
resolvers[key] = async (parent, params, context, resolveInfo) => {
|
||||
if (typeof parent[key] !== 'undefined') return parent[key]
|
||||
const id = parent[idAttribute]
|
||||
const statement = `
|
||||
MATCH(u:${type} {${idAttribute}: {id}})${connection}
|
||||
WHERE NOT related.deleted = true AND NOT related.disabled = true
|
||||
RETURN COUNT(DISTINCT(related)) as count
|
||||
`
|
||||
const result = await instance.cypher(statement, { id })
|
||||
const [response] = result.records.map(r => r.get('count').toNumber())
|
||||
return response
|
||||
}
|
||||
}
|
||||
return resolvers
|
||||
}
|
||||
|
||||
const hasManyResolver = obj => {
|
||||
const resolvers = {}
|
||||
for (const [key, connection] of Object.entries(obj)) {
|
||||
resolvers[key] = _hasResolver(resolvers, { key, connection }, { returnType: 'iterable' })
|
||||
}
|
||||
return resolvers
|
||||
}
|
||||
|
||||
const hasOneResolver = obj => {
|
||||
const resolvers = {}
|
||||
for (const [key, connection] of Object.entries(obj)) {
|
||||
resolvers[key] = _hasResolver(resolvers, { key, connection }, { returnType: 'object' })
|
||||
}
|
||||
return resolvers
|
||||
}
|
||||
const result = {
|
||||
...undefinedToNullResolver(undefinedToNull),
|
||||
...countResolver(count),
|
||||
...hasOneResolver(hasOne),
|
||||
...hasManyResolver(hasMany),
|
||||
}
|
||||
return result
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import { neode } from '../../bootstrap/neo4j'
|
||||
import Resolver from './helpers/Resolver'
|
||||
|
||||
const instance = neode()
|
||||
|
||||
@ -14,13 +15,6 @@ export default {
|
||||
|
||||
return response
|
||||
},
|
||||
DeleteSocialMedia: async (object, params, context, resolveInfo) => {
|
||||
const socialMedia = await instance.find('SocialMedia', params.id)
|
||||
const response = await socialMedia.toJson()
|
||||
await socialMedia.delete()
|
||||
|
||||
return response
|
||||
},
|
||||
UpdateSocialMedia: async (object, params, context, resolveInfo) => {
|
||||
const socialMedia = await instance.find('SocialMedia', params.id)
|
||||
await socialMedia.update({ url: params.url })
|
||||
@ -28,5 +22,17 @@ export default {
|
||||
|
||||
return response
|
||||
},
|
||||
DeleteSocialMedia: async (object, { id }, context, resolveInfo) => {
|
||||
const socialMedia = await instance.find('SocialMedia', id)
|
||||
if (!socialMedia) return null
|
||||
await socialMedia.delete()
|
||||
return socialMedia.toJson()
|
||||
},
|
||||
},
|
||||
SocialMedia: Resolver('SocialMedia', {
|
||||
idAttribute: 'url',
|
||||
hasOne: {
|
||||
ownedBy: '<-[:OWNED_BY]-(related:User)',
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
@ -122,6 +122,32 @@ describe('SocialMedia', () => {
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ownedBy', () => {
|
||||
beforeEach(() => {
|
||||
mutation = gql`
|
||||
mutation($url: String!) {
|
||||
CreateSocialMedia(url: $url) {
|
||||
url
|
||||
ownedBy {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
||||
|
||||
it('resolves', async () => {
|
||||
const user = someUser
|
||||
await expect(socialMediaAction(user, mutation, variables)).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
CreateSocialMedia: { url, ownedBy: { name: 'Kalle Blomqvist' } },
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('update social media', () => {
|
||||
|
||||
@ -2,57 +2,10 @@ import { neo4jgraphql } from 'neo4j-graphql-js'
|
||||
import fileUpload from './fileUpload'
|
||||
import { neode } from '../../bootstrap/neo4j'
|
||||
import { UserInputError } from 'apollo-server'
|
||||
import { undefinedToNull } from '../helpers'
|
||||
import Resolver from './helpers/Resolver'
|
||||
|
||||
const instance = neode()
|
||||
|
||||
const _has = (resolvers, { key, connection }, { returnType }) => {
|
||||
return async (parent, params, context, resolveInfo) => {
|
||||
if (typeof parent[key] !== 'undefined') return parent[key]
|
||||
const { id } = parent
|
||||
const statement = `MATCH(u:User {id: {id}})${connection} RETURN related`
|
||||
const result = await instance.cypher(statement, { id })
|
||||
let response = result.records.map(r => r.get('related').properties)
|
||||
if (returnType === 'object') response = response[0] || null
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
const count = obj => {
|
||||
const resolvers = {}
|
||||
for (const [key, connection] of Object.entries(obj)) {
|
||||
resolvers[key] = async (parent, params, context, resolveInfo) => {
|
||||
if (typeof parent[key] !== 'undefined') return parent[key]
|
||||
const { id } = parent
|
||||
const statement = `
|
||||
MATCH(u:User {id: {id}})${connection}
|
||||
WHERE NOT related.deleted = true AND NOT related.disabled = true
|
||||
RETURN COUNT(DISTINCT(related)) as count
|
||||
`
|
||||
const result = await instance.cypher(statement, { id })
|
||||
const [response] = result.records.map(r => r.get('count').toNumber())
|
||||
return response
|
||||
}
|
||||
}
|
||||
return resolvers
|
||||
}
|
||||
|
||||
export const hasMany = obj => {
|
||||
const resolvers = {}
|
||||
for (const [key, connection] of Object.entries(obj)) {
|
||||
resolvers[key] = _has(resolvers, { key, connection }, { returnType: 'iterable' })
|
||||
}
|
||||
return resolvers
|
||||
}
|
||||
|
||||
export const hasOne = obj => {
|
||||
const resolvers = {}
|
||||
for (const [key, connection] of Object.entries(obj)) {
|
||||
resolvers[key] = _has(resolvers, { key, connection }, { returnType: 'object' })
|
||||
}
|
||||
return resolvers
|
||||
}
|
||||
|
||||
export default {
|
||||
Query: {
|
||||
User: async (object, args, context, resolveInfo) => {
|
||||
@ -110,42 +63,44 @@ export default {
|
||||
let [{ email }] = result.records.map(r => r.get('e').properties)
|
||||
return email
|
||||
},
|
||||
...undefinedToNull([
|
||||
'actorId',
|
||||
'avatar',
|
||||
'coverImg',
|
||||
'deleted',
|
||||
'disabled',
|
||||
'locationName',
|
||||
'about',
|
||||
]),
|
||||
...count({
|
||||
contributionsCount: '-[:WROTE]->(related:Post)',
|
||||
friendsCount: '<-[:FRIENDS]->(related:User)',
|
||||
followingCount: '-[:FOLLOWS]->(related:User)',
|
||||
followedByCount: '<-[:FOLLOWS]-(related:User)',
|
||||
commentsCount: '-[:WROTE]->(r:Comment)',
|
||||
commentedCount: '-[:WROTE]->(:Comment)-[:COMMENTS]->(related:Post)',
|
||||
shoutedCount: '-[:SHOUTED]->(related:Post)',
|
||||
badgesCount: '<-[:REWARDED]-(related:Badge)',
|
||||
}),
|
||||
...hasOne({
|
||||
invitedBy: '<-[:INVITED]-(related:User)',
|
||||
disabledBy: '<-[:DISABLED]-(related:User)',
|
||||
}),
|
||||
...hasMany({
|
||||
followedBy: '<-[:FOLLOWS]-(related:User)',
|
||||
following: '-[:FOLLOWS]->(related:User)',
|
||||
friends: '-[:FRIENDS]-(related:User)',
|
||||
blacklisted: '-[:BLACKLISTED]->(related:User)',
|
||||
socialMedia: '-[:OWNED_BY]->(related:SocialMedia',
|
||||
contributions: '-[:WROTE]->(related:Post)',
|
||||
comments: '-[:WROTE]->(related:Comment)',
|
||||
shouted: '-[:SHOUTED]->(related:Post)',
|
||||
organizationsCreated: '-[:CREATED_ORGA]->(related:Organization)',
|
||||
organizationsOwned: '-[:OWNING_ORGA]->(related:Organization)',
|
||||
categories: '-[:CATEGORIZED]->(related:Category)',
|
||||
badges: '<-[:REWARDED]-(related:Badge)',
|
||||
...Resolver('User', {
|
||||
undefinedToNull: [
|
||||
'actorId',
|
||||
'avatar',
|
||||
'coverImg',
|
||||
'deleted',
|
||||
'disabled',
|
||||
'locationName',
|
||||
'about',
|
||||
],
|
||||
count: {
|
||||
contributionsCount: '-[:WROTE]->(related:Post)',
|
||||
friendsCount: '<-[:FRIENDS]->(related:User)',
|
||||
followingCount: '-[:FOLLOWS]->(related:User)',
|
||||
followedByCount: '<-[:FOLLOWS]-(related:User)',
|
||||
commentsCount: '-[:WROTE]->(r:Comment)',
|
||||
commentedCount: '-[:WROTE]->(:Comment)-[:COMMENTS]->(related:Post)',
|
||||
shoutedCount: '-[:SHOUTED]->(related:Post)',
|
||||
badgesCount: '<-[:REWARDED]-(related:Badge)',
|
||||
},
|
||||
hasOne: {
|
||||
invitedBy: '<-[:INVITED]-(related:User)',
|
||||
disabledBy: '<-[:DISABLED]-(related:User)',
|
||||
},
|
||||
hasMany: {
|
||||
followedBy: '<-[:FOLLOWS]-(related:User)',
|
||||
following: '-[:FOLLOWS]->(related:User)',
|
||||
friends: '-[:FRIENDS]-(related:User)',
|
||||
blacklisted: '-[:BLACKLISTED]->(related:User)',
|
||||
socialMedia: '-[:OWNED_BY]->(related:SocialMedia',
|
||||
contributions: '-[:WROTE]->(related:Post)',
|
||||
comments: '-[:WROTE]->(related:Comment)',
|
||||
shouted: '-[:SHOUTED]->(related:Post)',
|
||||
organizationsCreated: '-[:CREATED_ORGA]->(related:Organization)',
|
||||
organizationsOwned: '-[:OWNING_ORGA]->(related:Organization)',
|
||||
categories: '-[:CATEGORIZED]->(related:Category)',
|
||||
badges: '<-[:REWARDED]-(related:Badge)',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
@ -131,8 +131,3 @@ type SharedInboxEndpoint {
|
||||
uri: String
|
||||
}
|
||||
|
||||
type SocialMedia {
|
||||
id: ID!
|
||||
url: String
|
||||
ownedBy: [User]! @relation(name: "OWNED_BY", direction: "IN")
|
||||
}
|
||||
|
||||
11
backend/src/schema/types/type/SocialMedia.gql
Normal file
11
backend/src/schema/types/type/SocialMedia.gql
Normal file
@ -0,0 +1,11 @@
|
||||
type SocialMedia {
|
||||
id: ID!
|
||||
url: String
|
||||
ownedBy: User! @relation(name: "OWNED_BY", direction: "IN")
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
CreateSocialMedia(id: ID, url: String!): SocialMedia
|
||||
UpdateSocialMedia(id: ID!, url: String!): SocialMedia
|
||||
DeleteSocialMedia(id: ID!): SocialMedia
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user