Implement 'GroupMember' resolver, a first step

This commit is contained in:
Wolfgang Huß 2022-08-17 12:11:52 +02:00
parent acaf3aacb8
commit 27b74eb9e1
5 changed files with 186 additions and 0 deletions

View File

@ -52,6 +52,37 @@ const isMySocialMedia = rule({
return socialMedia.ownedBy.node.id === user.id
})
const isAllowSeeingMembersOfGroup = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
if (!user) return false
const { id: groupId } = args
const session = driver.session()
const readTxPromise = session.readTransaction(async (transaction) => {
const transactionResponse = await transaction.run(
`
MATCH (group:Group {id: $groupId})
OPTIONAL MATCH (admin {id:User $userId})-[membership:MEMBER_OF]->(group)
WHERE membership.role IN ['admin', 'owner']
RETURN group, admin
`,
{ groupId, userId: user.id },
)
return {
admin: transactionResponse.records.map((record) => record.get('admin')),
group: transactionResponse.records.map((record) => record.get('group')),
}
})
try {
const [{ admin, group }] = await readTxPromise
return group.groupType === 'public' || !!admin
} catch (error) {
throw new Error(error)
} finally {
session.close()
}
})
const isAuthor = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
@ -115,6 +146,7 @@ export default shield(
statistics: allow,
currentUser: allow,
Group: isAuthenticated,
GroupMember: isAllowSeeingMembersOfGroup,
Post: allow,
profilePagePosts: allow,
Comment: allow,

View File

@ -46,6 +46,28 @@ export default {
session.close()
}
},
GroupMember: async (_object, params, context, _resolveInfo) => {
const { id: groupId } = params
const session = context.driver.session()
const readTxResultPromise = session.readTransaction(async (txc) => {
const groupMemberCypher = `
MATCH (user:User {id: $userId})-[membership:MEMBER_OF]->(:Group {id: $groupId})
RETURN user {.*, myRoleInGroup: membership.role}
`
const result = await txc.run(groupMemberCypher, {
groupId,
userId: context.user.id,
})
return result.records.map((record) => record.get('user'))
})
try {
return await readTxResultPromise
} catch (error) {
throw new Error(error)
} finally {
session.close()
}
},
},
Mutation: {
CreateGroup: async (_parent, params, context, _resolveInfo) => {

View File

@ -207,6 +207,128 @@ describe('Group', () => {
})
})
describe('GroupMember', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await query({ query: groupQuery, variables: {} })
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
describe('authenticated', () => {
beforeEach(async () => {
authenticatedUser = await user.toJson()
})
let otherUser
beforeEach(async () => {
otherUser = await Factory.build(
'user',
{
id: 'other-user',
name: 'Other TestUser',
},
{
email: 'test2@example.org',
password: '1234',
},
)
authenticatedUser = await otherUser.toJson()
await mutate({
mutation: createGroupMutation,
variables: {
id: 'others-group',
name: 'Uninteresting Group',
about: 'We will change nothing!',
description: 'We love it like it is!?' + descriptionAdditional100,
groupType: 'closed',
actionRadius: 'global',
categoryIds,
},
})
authenticatedUser = await user.toJson()
await mutate({
mutation: createGroupMutation,
variables: {
id: 'my-group',
name: 'The Best Group',
about: 'We will change the world!',
description: 'Some description' + descriptionAdditional100,
groupType: 'public',
actionRadius: 'regional',
categoryIds,
},
})
})
describe('query group members', () => {
describe('by owner', () => {
it.only('finds all members', async () => {
const expected = {
data: {
GroupMember: expect.arrayContaining([
expect.objectContaining({
id: 'my-group',
slug: 'the-best-group',
myRole: 'owner',
}),
// Wolle: expect.objectContaining({
// id: 'others-group',
// slug: 'uninteresting-group',
// myRole: null,
// }),
]),
},
errors: undefined,
}
await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject(expected)
})
})
describe('isMember = true', () => {
it('finds only groups where user is member', async () => {
const expected = {
data: {
Group: [
{
id: 'my-group',
slug: 'the-best-group',
myRole: 'owner',
},
],
},
errors: undefined,
}
await expect(
query({ query: groupQuery, variables: { isMember: true } }),
).resolves.toMatchObject(expected)
})
})
describe('isMember = false', () => {
it('finds only groups where user is not(!) member', async () => {
const expected = {
data: {
Group: expect.arrayContaining([
expect.objectContaining({
id: 'others-group',
slug: 'uninteresting-group',
myRole: null,
}),
]),
},
errors: undefined,
}
await expect(
query({ query: groupQuery, variables: { isMember: false } }),
).resolves.toMatchObject(expected)
})
})
})
})
})
describe('CreateGroup', () => {
beforeEach(() => {
variables = {

View File

@ -74,6 +74,14 @@ type Query {
filter: _GroupFilter
): [Group]
GroupMember(
id: ID
first: Int
offset: Int
orderBy: [_GroupOrdering]
filter: _GroupFilter
): [User]
AvailableGroupTypes: [GroupType]!
AvailableGroupActionRadii: [GroupActionRadius]!

View File

@ -114,6 +114,8 @@ type User {
badgesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)")
emotions: [EMOTED]
myRoleInGroup: GroupMemberRole
}