Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

feat: 🍰 List All Groups #5582

Merged
merged 8 commits into from
Oct 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/src/middleware/permissionsMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ export default shield(
currentUser: allow,
Group: isAuthenticated,
GroupMembers: isAllowedSeeingGroupMembers,
GroupCount: isAuthenticated,
Post: allow,
profilePagePosts: allow,
Comment: allow,
Expand Down
40 changes: 39 additions & 1 deletion backend/src/schema/resolvers/groups.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import { createOrUpdateLocations } from './users/location'
export default {
Query: {
Group: async (_object, params, context, _resolveInfo) => {
const { isMember, id, slug } = params
const { isMember, id, slug, first, offset } = params
let pagination = ''
if (first !== undefined && offset !== undefined) pagination = `SKIP ${offset} LIMIT ${first}`
const matchParams = { id, slug }
removeUndefinedNullValuesFromObject(matchParams)
const session = context.driver.session()
Expand All @@ -27,6 +29,7 @@ export default {
WITH group, membership
WHERE (group.groupType IN ['public', 'closed']) OR (group.groupType = 'hidden' AND membership.role IN ['usual', 'admin', 'owner'])
RETURN group {.*, myRole: membership.role}
${pagination}
`
} else {
if (isMember === false) {
Expand All @@ -36,6 +39,7 @@ export default {
WITH group
WHERE group.groupType IN ['public', 'closed']
RETURN group {.*, myRole: NULL}
${pagination}
`
} else {
groupCypher = `
Expand All @@ -44,6 +48,7 @@ export default {
WITH group, membership
WHERE (group.groupType IN ['public', 'closed']) OR (group.groupType = 'hidden' AND membership.role IN ['usual', 'admin', 'owner'])
RETURN group {.*, myRole: membership.role}
${pagination}
`
}
}
Expand Down Expand Up @@ -81,6 +86,39 @@ export default {
session.close()
}
},
GroupCount: async (_object, params, context, _resolveInfo) => {
const { isMember } = params
const {
user: { id: userId },
} = context
const session = context.driver.session()
const readTxResultPromise = session.readTransaction(async (txc) => {
let cypher
if (isMember) {
cypher = `MATCH (user:User)-[membership:MEMBER_OF]->(group:Group)
WHERE user.id = $userId
AND membership.role IN ['usual', 'admin', 'owner']
RETURN toString(count(group)) AS count`
} else {
cypher = `MATCH (group:Group)
OPTIONAL MATCH (user:User)-[membership:MEMBER_OF]->(group)
WHERE user.id = $userId
WITH group, membership
WHERE group.groupType IN ['public', 'closed']
OR membership.role IN ['usual', 'admin', 'owner']
RETURN toString(count(group)) AS count`
}
const transactionResponse = await txc.run(cypher, { userId })
return transactionResponse.records.map((record) => record.get('count'))
})
try {
return parseInt(await readTxResultPromise)
} catch (error) {
throw new Error(error)
} finally {
session.close()
}
},
},
Mutation: {
CreateGroup: async (_parent, params, context, _resolveInfo) => {
Expand Down
6 changes: 4 additions & 2 deletions backend/src/schema/types/type/Group.gql
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ type Query {
isMember: Boolean # if 'undefined' or 'null' then get all groups
id: ID
slug: String
# first: Int # not implemented yet
# offset: Int # not implemented yet
first: Int
offset: Int
# orderBy: [_GroupOrdering] # not implemented yet
# filter: _GroupFilter # not implemented yet
): [Group]
Expand All @@ -79,6 +79,8 @@ type Query {
# filter: _UserFilter # not implemented yet
): [User]

GroupCount(isMember: Boolean): Int

# AvailableGroupTypes: [GroupType]!

# AvailableGroupActionRadii: [GroupActionRadius]!
Expand Down
4 changes: 2 additions & 2 deletions webapp/components/AvatarMenu/AvatarMenu.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ describe('AvatarMenu.vue', () => {
expect(profileLink.exists()).toBe(true)
})

it('displays a link to "My groups"', () => {
it('displays a link to "Groups"', () => {
const profileLink = wrapper
.findAll('.ds-menu-item span')
.at(wrapper.vm.routes.findIndex((route) => route.path === '/my-groups'))
.at(wrapper.vm.routes.findIndex((route) => route.path === '/groups'))
expect(profileLink.exists()).toBe(true)
})

Expand Down
4 changes: 2 additions & 2 deletions webapp/components/AvatarMenu/AvatarMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ export default {
icon: 'user',
},
{
name: this.$t('header.avatarMenu.myGroups'),
path: '/my-groups',
name: this.$t('header.avatarMenu.Groups'),
path: '/groups',
icon: 'users',
},
{
Expand Down
2 changes: 1 addition & 1 deletion webapp/components/Group/GroupButton.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div>
<nuxt-link to="/my-groups"><base-button icon="users" circle ghost /></nuxt-link>
<nuxt-link to="/groups"><base-button icon="users" circle ghost /></nuxt-link>
</div>
</template>
2 changes: 1 addition & 1 deletion webapp/components/Group/GroupForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@

<!-- submit -->
<ds-space margin-top="large">
<nuxt-link to="/my-groups">
<nuxt-link to="/groups">
<ds-button>{{ $t('actions.cancel') }}</ds-button>
</nuxt-link>
<ds-button type="submit" icon="save" primary :disabled="checkFormError(errors)" fill>
Expand Down
12 changes: 10 additions & 2 deletions webapp/graphql/groups.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@ export const changeGroupMemberRoleMutation = () => {
export const groupQuery = (i18n) => {
const lang = i18n ? i18n.locale().toUpperCase() : 'EN'
return gql`
query ($isMember: Boolean, $id: ID, $slug: String) {
Group(isMember: $isMember, id: $id, slug: $slug) {
query ($isMember: Boolean, $id: ID, $slug: String, $first: Int, $offset: Int) {
Group(isMember: $isMember, id: $id, slug: $slug, first: $first, offset: $offset) {
id
name
slug
Expand Down Expand Up @@ -190,3 +190,11 @@ export const groupMembersQuery = () => {
}
`
}

export const groupCountQuery = () => {
return gql`
query ($isMember: Boolean) {
GroupCount(isMember: $isMember)
}
`
}
3 changes: 2 additions & 1 deletion webapp/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@
"addMemberToGroup": "Zur Gruppe hinzufügen",
"addUser": "Benutzer hinzufügen",
"addUserPlaceholder": "eindeutiger Benutzername > @slug-from-user",
"allGroups": "Alle Gruppen",
"categories": "Thema ::: Themen",
"changeMemberRole": "Die Rolle wurde auf „{role}“ geändert!",
"contentMenu": {
Expand Down Expand Up @@ -476,7 +477,7 @@
},
"header": {
"avatarMenu": {
"myGroups": "Mein Gruppen",
"Groups": "Gruppen",
"myProfile": "Mein Profil"
}
},
Expand Down
3 changes: 2 additions & 1 deletion webapp/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@
"addMemberToGroup": "Add to group",
"addUser": "Add User",
"addUserPlaceholder": "unique username > @slug-from-user",
"allGroups": "All Groups",
"categories": "Topic ::: Topics",
"changeMemberRole": "The role has been changed to “{role}”!",
"contentMenu": {
Expand Down Expand Up @@ -476,7 +477,7 @@
},
"header": {
"avatarMenu": {
"myGroups": "My groups",
"Groups": "Groups",
"myProfile": "My profile"
}
},
Expand Down
1 change: 0 additions & 1 deletion webapp/pages/group/create.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export default {
},
})
this.$toast.success(this.$t('group.groupCreated'))
// this.$router.history.push('/my-groups')
this.$router.history.push({
name: 'group-id-slug',
params: { id: responseId, slug: responseSlug },
Expand Down
35 changes: 35 additions & 0 deletions webapp/pages/groups.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { config, mount } from '@vue/test-utils'
import groups from './groups.vue'

const localVue = global.localVue

config.stubs['nuxt-link'] = '<span class="nuxt-link"><slot /></span>'
config.stubs['client-only'] = '<span class="client-only"><slot /></span>'

describe('groups', () => {
let wrapper
let mocks

beforeEach(() => {
mocks = {
$t: jest.fn(),
}
})

describe('mount', () => {
const Wrapper = () => {
return mount(groups, {
mocks,
localVue,
})
}

beforeEach(() => {
wrapper = Wrapper()
})

it('renders', () => {
expect(wrapper.is('div')).toBe(true)
})
})
})
Loading