Merge branch '5059-groups/5131-implement-group-gql-model-and-crud' into 5140-My-Groups-Page

This commit is contained in:
ogerly 2022-08-09 09:02:38 +02:00
commit dc2f5f255d
6 changed files with 256 additions and 199 deletions

View File

@ -0,0 +1,5 @@
// this file is duplicated in `backend/src/config/metadata.js` and `webapp/constants/metadata.js`
export default {
CATEGORIES_MIN: 1,
CATEGORIES_MAX: 3,
}

View File

@ -4,13 +4,13 @@ import gql from 'graphql-tag'
export const createGroupMutation = gql`
mutation (
$id: ID,
$name: String!,
$slug: String,
$about: String,
$description: String!,
$groupType: GroupType!,
$actionRadius: GroupActionRadius!,
$id: ID
$name: String!
$slug: String
$about: String
$description: String!
$groupType: GroupType!
$actionRadius: GroupActionRadius!
$categoryIds: [ID]
) {
CreateGroup(
@ -47,13 +47,13 @@ export const createGroupMutation = gql`
export const groupQuery = gql`
query (
$isMember: Boolean
$id: ID,
$name: String,
$slug: String,
$id: ID
$name: String
$slug: String
$createdAt: String
$updatedAt: String
$about: String,
$description: String,
$about: String
$description: String
# $groupType: GroupType!,
# $actionRadius: GroupActionRadius!,
# $categoryIds: [ID]
@ -93,6 +93,12 @@ export const groupQuery = gql`
groupType
actionRadius
myRole
categories {
id
slug
name
icon
}
# Wolle: owner {
# name
# }

View File

@ -1,5 +1,6 @@
// TODO: can be replaced with, which is no a fake:
// TODO: can be replaced with: (which is no a fake)
// import gql from 'graphql-tag'
// See issue: https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/5152
//* This is a fake ES2015 template string, just to benefit of syntax
// highlighting of `gql` template strings in certain editors.

View File

@ -3,6 +3,7 @@ import { v4 as uuid } from 'uuid'
// Wolle: import { isEmpty } from 'lodash'
import { UserInputError } from 'apollo-server'
import CONFIG from '../../config'
import categories from '../../constants/categories'
// Wolle: import { mergeImage, deleteImage } from './images/images'
import Resolver from './helpers/Resolver'
// Wolle: import { filterForMutedUsers } from './helpers/filterForMutedUsers'
@ -69,6 +70,12 @@ export default {
CreateGroup: async (_parent, params, context, _resolveInfo) => {
const { categoryIds } = params
delete params.categoryIds
if (!categoryIds || categoryIds.length < categories.CATEGORIES_MIN) {
throw new UserInputError('To Less Categories!')
}
if (categoryIds && categoryIds.length > categories.CATEGORIES_MAX) {
throw new UserInputError('To Many Categories!')
}
params.id = params.id || uuid()
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async (transaction) => {

View File

@ -52,21 +52,25 @@ beforeEach(async () => {
neode.create('Category', {
id: 'cat9',
name: 'Democracy & Politics',
slug: 'democracy-politics',
icon: 'university',
}),
neode.create('Category', {
id: 'cat4',
name: 'Environment & Nature',
slug: 'environment-nature',
icon: 'tree',
}),
neode.create('Category', {
id: 'cat15',
name: 'Consumption & Sustainability',
slug: 'consumption-sustainability',
icon: 'shopping-cart',
}),
neode.create('Category', {
id: 'cat27',
name: 'Animal Protection',
slug: 'animal-protection',
icon: 'paw',
}),
])
@ -133,44 +137,8 @@ describe('Group', () => {
})
})
describe('query can fetch', () => {
it('groups where user is member (or owner in this case)', 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)
})
it('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)
})
it('all groups', async () => {
describe('can find', () => {
it('all', async () => {
const expected = {
data: {
Group: expect.arrayContaining([
@ -190,164 +158,200 @@ describe('Group', () => {
}
await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject(expected)
})
it('where user is member (or owner in this case)', 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)
})
it('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)
})
})
// Wolle: describe('can be filtered', () => {
// let followedUser, happyPost, cryPost
// beforeEach(async () => {
// ;[followedUser] = await Promise.all([
// Factory.build(
// 'user',
// {
// id: 'followed-by-me',
// name: 'Followed User',
// },
// {
// email: 'followed@example.org',
// password: '1234',
// },
// ),
// ])
// ;[happyPost, cryPost] = await Promise.all([
// Factory.build('post', { id: 'happy-post' }, { categoryIds: ['cat4'] }),
// Factory.build('post', { id: 'cry-post' }, { categoryIds: ['cat15'] }),
// Factory.build(
// 'post',
// {
// id: 'post-by-followed-user',
// },
// {
// categoryIds: ['cat9'],
// author: followedUser,
// },
// ),
// ])
// })
// describe('no filter', () => {
// it('returns all posts', async () => {
// const postQueryNoFilters = gql`
// query Post($filter: _PostFilter) {
// Post(filter: $filter) {
// id
// }
// }
// `
// const expected = [{ id: 'happy-post' }, { id: 'cry-post' }, { id: 'post-by-followed-user' }]
// variables = { filter: {} }
// await expect(query({ query: postQueryNoFilters, variables })).resolves.toMatchObject({
// data: {
// Post: expect.arrayContaining(expected),
// },
// })
// })
// })
// /* it('by categories', async () => {
// const postQueryFilteredByCategories = gql`
// query Post($filter: _PostFilter) {
// Post(filter: $filter) {
// id
// categories {
// id
// }
// }
// describe('can be filtered', () => {
// Wolle: it('by categories', async () => {
// const postQueryFilteredByCategories = gql`
// query Post($filter: _PostFilter) {
// Post(filter: $filter) {
// id
// categories {
// id
// }
// `
// const expected = {
// data: {
// Post: [
// {
// id: 'post-by-followed-user',
// categories: [{ id: 'cat9' }],
// },
// ],
// },
// }
// variables = { ...variables, filter: { categories_some: { id_in: ['cat9'] } } }
// await expect(
// query({ query: postQueryFilteredByCategories, variables }),
// ).resolves.toMatchObject(expected)
// }) */
// describe('by emotions', () => {
// const postQueryFilteredByEmotions = gql`
// query Post($filter: _PostFilter) {
// Post(filter: $filter) {
// id
// emotions {
// emotion
// }
// }
// }
// `
// const expected = {
// data: {
// Post: [
// {
// id: 'post-by-followed-user',
// categories: [{ id: 'cat9' }],
// },
// ],
// },
// }
// variables = { ...variables, filter: { categories_some: { id_in: ['cat9'] } } }
// await expect(
// query({ query: postQueryFilteredByCategories, variables }),
// ).resolves.toMatchObject(expected)
// })
// Wolle: let followedUser, happyPost, cryPost
// beforeEach(async () => {
// ;[followedUser] = await Promise.all([
// Factory.build(
// 'user',
// {
// id: 'followed-by-me',
// name: 'Followed User',
// },
// {
// email: 'followed@example.org',
// password: '1234',
// },
// ),
// ])
// ;[happyPost, cryPost] = await Promise.all([
// Factory.build('post', { id: 'happy-post' }, { categoryIds: ['cat4'] }),
// Factory.build('post', { id: 'cry-post' }, { categoryIds: ['cat15'] }),
// Factory.build(
// 'post',
// {
// id: 'post-by-followed-user',
// },
// {
// categoryIds: ['cat9'],
// author: followedUser,
// },
// ),
// ])
// })
// describe('no filter', () => {
// it('returns all posts', async () => {
// const postQueryNoFilters = gql`
// query Post($filter: _PostFilter) {
// Post(filter: $filter) {
// id
// }
// `
// it('filters by single emotion', async () => {
// const expected = {
// data: {
// Post: [
// {
// id: 'happy-post',
// emotions: [{ emotion: 'happy' }],
// },
// ],
// },
// }
// `
// const expected = [{ id: 'happy-post' }, { id: 'cry-post' }, { id: 'post-by-followed-user' }]
// variables = { filter: {} }
// await expect(query({ query: postQueryNoFilters, variables })).resolves.toMatchObject({
// data: {
// Post: expect.arrayContaining(expected),
// },
// })
// })
// })
// describe('by emotions', () => {
// const postQueryFilteredByEmotions = gql`
// query Post($filter: _PostFilter) {
// Post(filter: $filter) {
// id
// emotions {
// emotion
// }
// await user.relateTo(happyPost, 'emoted', { emotion: 'happy' })
// variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy'] } } }
// await expect(
// query({ query: postQueryFilteredByEmotions, variables }),
// ).resolves.toMatchObject(expected)
// })
// it('filters by multiple emotions', async () => {
// const expected = [
// }
// }
// `
// it('filters by single emotion', async () => {
// const expected = {
// data: {
// Post: [
// {
// id: 'happy-post',
// emotions: [{ emotion: 'happy' }],
// },
// {
// id: 'cry-post',
// emotions: [{ emotion: 'cry' }],
// },
// ]
// await user.relateTo(happyPost, 'emoted', { emotion: 'happy' })
// await user.relateTo(cryPost, 'emoted', { emotion: 'cry' })
// variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy', 'cry'] } } }
// await expect(
// query({ query: postQueryFilteredByEmotions, variables }),
// ).resolves.toMatchObject({
// data: {
// Post: expect.arrayContaining(expected),
// },
// errors: undefined,
// })
// })
// })
// it('by followed-by', async () => {
// const postQueryFilteredByUsersFollowed = gql`
// query Post($filter: _PostFilter) {
// Post(filter: $filter) {
// id
// author {
// id
// name
// }
// }
// }
// `
// await user.relateTo(followedUser, 'following')
// variables = { filter: { author: { followedBy_some: { id: 'current-user' } } } }
// await expect(
// query({ query: postQueryFilteredByUsersFollowed, variables }),
// ).resolves.toMatchObject({
// data: {
// Post: [
// {
// id: 'post-by-followed-user',
// author: { id: 'followed-by-me', name: 'Followed User' },
// },
// ],
// },
// errors: undefined,
// })
// ],
// },
// }
// await user.relateTo(happyPost, 'emoted', { emotion: 'happy' })
// variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy'] } } }
// await expect(
// query({ query: postQueryFilteredByEmotions, variables }),
// ).resolves.toMatchObject(expected)
// })
// it('filters by multiple emotions', async () => {
// const expected = [
// {
// id: 'happy-post',
// emotions: [{ emotion: 'happy' }],
// },
// {
// id: 'cry-post',
// emotions: [{ emotion: 'cry' }],
// },
// ]
// await user.relateTo(happyPost, 'emoted', { emotion: 'happy' })
// await user.relateTo(cryPost, 'emoted', { emotion: 'cry' })
// variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy', 'cry'] } } }
// await expect(
// query({ query: postQueryFilteredByEmotions, variables }),
// ).resolves.toMatchObject({
// data: {
// Post: expect.arrayContaining(expected),
// },
// errors: undefined,
// })
// })
// })
// it('by followed-by', async () => {
// const postQueryFilteredByUsersFollowed = gql`
// query Post($filter: _PostFilter) {
// Post(filter: $filter) {
// id
// author {
// id
// name
// }
// }
// }
// `
// await user.relateTo(followedUser, 'following')
// variables = { filter: { author: { followedBy_some: { id: 'current-user' } } } }
// await expect(
// query({ query: postQueryFilteredByUsersFollowed, variables }),
// ).resolves.toMatchObject({
// data: {
// Post: [
// {
// id: 'post-by-followed-user',
// author: { id: 'followed-by-me', name: 'Followed User' },
// },
// ],
// },
// errors: undefined,
// })
// })
// })
})
})
@ -412,12 +416,34 @@ describe('CreateGroup', () => {
)
})
it('`disabled` and `deleted` default to `false`', async () => {
it('"disabled" and "deleted" default to "false"', async () => {
const expected = { data: { CreateGroup: { disabled: false, deleted: false } } }
await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
expected,
)
})
describe('categories', () => {
describe('not even one', () => {
it('throws error: "To Less Categories!"', async () => {
const { errors } = await mutate({
mutation: createGroupMutation,
variables: { ...variables, categoryIds: null },
})
expect(errors[0]).toHaveProperty('message', 'To Less Categories!')
})
})
describe('four', () => {
it('throws error: "To Many Categories!"', async () => {
const { errors } = await mutate({
mutation: createGroupMutation,
variables: { ...variables, categoryIds: ['cat9', 'cat4', 'cat15', 'cat27'] },
})
expect(errors[0]).toHaveProperty('message', 'To Many Categories!')
})
})
})
})
})

View File

@ -141,6 +141,14 @@ input _GroupFilter {
id_not: ID
id_in: [ID!]
id_not_in: [ID!]
# categories: _CategoryFilter
# categories_not: _CategoryFilter
# categories_in: [_CategoryFilter!]
# categories_not_in: [_CategoryFilter!]
# categories_some: _CategoryFilter
# categories_none: _CategoryFilter
# categories_single: _CategoryFilter
# categories_every: _CategoryFilter
# Wolle:
# friends: _GroupFilter
# friends_not: _GroupFilter
@ -185,7 +193,11 @@ type Query {
filter: _GroupFilter
): [Group]
availableGroupTypes: [GroupType]!
AvailableGroupTypes: [GroupType]!
AvailableGroupActionRadii: [GroupActionRadius]!
AvailableGroupMemberRoles: [GroupMemberRole]!
# Wolle:
# availableRoles: [UserRole]!