Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: 🍰 Implement Group GQL Model And CRUD Resolvers – First Step #5139

Merged
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
bf9dd20
Implement GQL for groups
Tirokk Aug 2, 2022
52bffa4
Fix linting
Tirokk Aug 2, 2022
9632d0f
Fix file name from 'Groups.js' to singular 'Group.js'
Tirokk Aug 2, 2022
f565e5f
Implement 'CreateGroup' with tests
Tirokk Aug 2, 2022
4f16465
Fix Group name slugification
Tirokk Aug 2, 2022
da2c7dc
Add migration with fulltext indeces and unique keys for groups
Tirokk Aug 3, 2022
fc20143
Test groups creation in slugifyMiddleware
Tirokk Aug 3, 2022
ea0223b
Rename 'UserGroup' to 'UserRole'
Tirokk Aug 3, 2022
2c402ba
Fix linting
Tirokk Aug 3, 2022
45558f0
Add comment about faking the 'gql' tag in the backend
Tirokk Aug 3, 2022
520598c
Implement 'description', 'groupType', and 'actionRadius' in 'CreateGr…
Tirokk Aug 3, 2022
cc44ee8
Refactor relations ':OWNS' and ':ADMINISTERS' to ':MEMBER_OF' with pr…
Tirokk Aug 3, 2022
9441164
Implement 'Group' query, first step
Tirokk Aug 3, 2022
867b78d
Implement 'Group' query, second step
Tirokk Aug 3, 2022
2107dd4
Merge branch 'master' of github.com:Ocelot-Social-Community/Ocelot-So…
Tirokk Aug 3, 2022
fcca0f3
Implement 'Group' query, next step
Tirokk Aug 4, 2022
504a9bc
Merge branch '5059-epic-groups' of github.com:Ocelot-Social-Community…
Tirokk Aug 4, 2022
b120565
Add issue to TODO comment
Tirokk Aug 9, 2022
7847d69
Return 'categories' in the 'Group' GQL query
Tirokk Aug 9, 2022
61344fc
Implement errors for to less or to many categories and test it
Tirokk Aug 9, 2022
b5678f7
Merge branch 'master' of github.com:Ocelot-Social-Community/Ocelot-So…
Tirokk Aug 9, 2022
117bd5e
Implement error for to short 'description' and test it
Tirokk Aug 9, 2022
78eeb7e
Merge branch '5059-epic-groups' of github.com:Ocelot-Social-Community…
Tirokk Aug 9, 2022
0149af1
Cleanup
Tirokk Aug 9, 2022
3d1b403
Add relation 'CREATED' between owner and group
Tirokk Aug 9, 2022
9cae32e
Upgrade neode to v0.4.8
Tirokk Aug 10, 2022
7682aa7
Fix description length for slugify tests
Tirokk Aug 10, 2022
82401b1
Update backend/src/schema/resolvers/groups.js
Tirokk Aug 10, 2022
f150ea3
Update backend/src/schema/resolvers/groups.js
Tirokk Aug 10, 2022
5e741ea
Overtake Moriz suggestions
Tirokk Aug 10, 2022
b0d28f8
Rename 'descriptionAddition100' to 'descriptionAdditional100'
Tirokk Aug 10, 2022
dd876c5
increas max-old-space-size for jest, handle some asyncs, test validat…
Mogge Aug 15, 2022
beacad4
set CONFIG in specs
Mogge Aug 15, 2022
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
2 changes: 1 addition & 1 deletion backend/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ AWS_BUCKET=
EMAIL_DEFAULT_SENDER="devops@ocelot.social"
EMAIL_SUPPORT="devops@ocelot.social"

CATEGORIES_ACTIVE=false
CATEGORIES_ACTIVE=false
2 changes: 1 addition & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
"mustache": "^4.2.0",
"neo4j-driver": "^4.0.2",
"neo4j-graphql-js": "^2.11.5",
"neode": "^0.4.7",
"neode": "^0.4.8",
"node-fetch": "~2.6.1",
"nodemailer": "^6.4.4",
"nodemailer-html-to-text": "^3.2.0",
Expand Down
3 changes: 3 additions & 0 deletions backend/src/constants/categories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// this file is duplicated in `backend/src/constants/metadata.js` and `webapp/constants/metadata.js`
export const CATEGORIES_MIN = 1
export const CATEGORIES_MAX = 3
2 changes: 2 additions & 0 deletions backend/src/constants/groups.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// this file is duplicated in `backend/src/constants/group.js` and `webapp/constants/group.js`
export const DESCRIPTION_WITHOUT_HTML_LENGTH_MIN = 100 // with removed HTML tags
29 changes: 29 additions & 0 deletions backend/src/db/graphql/authentications.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import gql from 'graphql-tag'

// ------ mutations

export const signupVerificationMutation = gql`
mutation (
$password: String!
$email: String!
$name: String!
$slug: String
$nonce: String!
$termsAndConditionsAgreedVersion: String!
) {
SignupVerification(
email: $email
password: $password
name: $name
slug: $slug
nonce: $nonce
termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
) {
slug
}
}
`

// ------ queries

// fill queries in here
95 changes: 95 additions & 0 deletions backend/src/db/graphql/groups.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import gql from 'graphql-tag'

// ------ mutations

export const createGroupMutation = gql`
mutation (
$id: ID
$name: String!
$slug: String
$about: String
$description: String!
$groupType: GroupType!
$actionRadius: GroupActionRadius!
$categoryIds: [ID]
) {
CreateGroup(
id: $id
name: $name
slug: $slug
about: $about
description: $description
groupType: $groupType
actionRadius: $actionRadius
categoryIds: $categoryIds
) {
id
name
slug
createdAt
updatedAt
disabled
deleted
about
description
groupType
actionRadius
myRole
}
}
`

// ------ queries

export const groupQuery = gql`
query (
$isMember: Boolean
$id: ID
$name: String
$slug: String
$createdAt: String
$updatedAt: String
$about: String
$description: String
$locationName: String
$first: Int
$offset: Int
$orderBy: [_GroupOrdering]
$filter: _GroupFilter
) {
Group(
isMember: $isMember
id: $id
name: $name
slug: $slug
createdAt: $createdAt
updatedAt: $updatedAt
about: $about
description: $description
locationName: $locationName
first: $first
offset: $offset
orderBy: $orderBy
filter: $filter
) {
id
name
slug
createdAt
updatedAt
disabled
deleted
about
description
groupType
actionRadius
myRole
categories {
id
slug
name
icon
}
}
}
`
15 changes: 15 additions & 0 deletions backend/src/db/graphql/posts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import gql from 'graphql-tag'

// ------ mutations

export const createPostMutation = gql`
mutation ($title: String!, $content: String!, $categoryIds: [ID]!, $slug: String) {
CreatePost(title: $title, content: $content, categoryIds: $categoryIds, slug: $slug) {
slug
}
}
`

// ------ queries

// fill queries in here
4 changes: 2 additions & 2 deletions backend/src/db/migrate/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ class Store {
const session = driver.session()
await createDefaultAdminUser(session)
const writeTxResultPromise = session.writeTransaction(async (txc) => {
await txc.run('CALL apoc.schema.assert({},{},true)') // drop all indices
await txc.run('CALL apoc.schema.assert({},{},true)') // drop all indices and contraints
return Promise.all(
[
'CALL db.index.fulltext.createNodeIndex("post_fulltext_search",["Post"],["title", "content"])',
'CALL db.index.fulltext.createNodeIndex("user_fulltext_search",["User"],["name", "slug"])',
'CALL db.index.fulltext.createNodeIndex("post_fulltext_search",["Post"],["title", "content"])',
'CALL db.index.fulltext.createNodeIndex("tag_fulltext_search",["Tag"],["id"])',
].map((statement) => txc.run(statement)),
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { getDriver } from '../../db/neo4j'

export const description = `
We introduced a new node label 'Group' and we need two primary keys 'id' and 'slug' for it.
Additional we like to have fulltext indices the keys 'name', 'slug', 'about', and 'description'.
`

export async function up(next) {
const driver = getDriver()
const session = driver.session()
const transaction = session.beginTransaction()

try {
// Implement your migration here.
await transaction.run(`
CREATE CONSTRAINT ON ( group:Group ) ASSERT group.id IS UNIQUE
`)
await transaction.run(`
CREATE CONSTRAINT ON ( group:Group ) ASSERT group.slug IS UNIQUE
`)
await transaction.run(`
CALL db.index.fulltext.createNodeIndex("group_fulltext_search",["Group"],["name", "slug", "about", "description"])
`)
await transaction.commit()
next()
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
await transaction.rollback()
// eslint-disable-next-line no-console
console.log('rolled back')
throw new Error(error)
} finally {
session.close()
}
}

export async function down(next) {
const driver = getDriver()
const session = driver.session()
const transaction = session.beginTransaction()

try {
// Implement your migration here.
await transaction.run(`
DROP CONSTRAINT ON ( group:Group ) ASSERT group.id IS UNIQUE
`)
await transaction.run(`
DROP CONSTRAINT ON ( group:Group ) ASSERT group.slug IS UNIQUE
`)
await transaction.run(`
CALL db.index.fulltext.drop("group_fulltext_search")
`)
await transaction.commit()
next()
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
await transaction.rollback()
// eslint-disable-next-line no-console
console.log('rolled back')
throw new Error(error)
} finally {
session.close()
}
}
4 changes: 4 additions & 0 deletions backend/src/helpers/jest.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// 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.
export function gql(strings) {
Expand Down
5 changes: 5 additions & 0 deletions backend/src/middleware/excerptMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import trunc from 'trunc-html'

export default {
Mutation: {
CreateGroup: async (resolve, root, args, context, info) => {
args.descriptionExcerpt = trunc(args.description, 120).html
const result = await resolve(root, args, context, info)
return result
},
CreatePost: async (resolve, root, args, context, info) => {
args.contentExcerpt = trunc(args.content, 120).html
const result = await resolve(root, args, context, info)
Expand Down
7 changes: 7 additions & 0 deletions backend/src/middleware/helpers/cleanHtml.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import sanitizeHtml from 'sanitize-html'
import linkifyHtml from 'linkifyjs/html'

export const removeHtmlTags = (input) => {
return sanitizeHtml(input, {
allowedTags: [],
allowedAttributes: {},
})
}

const standardSanitizeHtmlOptions = {
allowedTags: [
'img',
Expand Down
9 changes: 1 addition & 8 deletions backend/src/middleware/languages/languages.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import LanguageDetect from 'languagedetect'
import sanitizeHtml from 'sanitize-html'

const removeHtmlTags = (input) => {
return sanitizeHtml(input, {
allowedTags: [],
allowedAttributes: {},
})
}
import { removeHtmlTags } from '../helpers/cleanHtml.js'

const setPostLanguage = (text) => {
const lngDetector = new LanguageDetect()
Expand Down
2 changes: 2 additions & 0 deletions backend/src/middleware/permissionsMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export default shield(
reports: isModerator,
statistics: allow,
currentUser: allow,
Group: isAuthenticated,
Post: allow,
profilePagePosts: allow,
Comment: allow,
Expand All @@ -140,6 +141,7 @@ export default shield(
Signup: or(publicRegistration, inviteRegistration, isAdmin),
SignupVerification: allow,
UpdateUser: onlyYourself,
CreateGroup: isAuthenticated,
CreatePost: isAuthenticated,
UpdatePost: isAuthor,
DeletePost: isAuthor,
Expand Down
4 changes: 4 additions & 0 deletions backend/src/middleware/sluggifyMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export default {
args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'User')))
return resolve(root, args, context, info)
},
CreateGroup: async (resolve, root, args, context, info) => {
args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'Group')))
return resolve(root, args, context, info)
},
CreatePost: async (resolve, root, args, context, info) => {
args.slug = args.slug || (await uniqueSlug(args.title, isUniqueFor(context, 'Post')))
return resolve(root, args, context, info)
Expand Down
1 change: 1 addition & 0 deletions backend/src/middleware/slugify/uniqueSlug.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import slugify from 'slug'

export default async function uniqueSlug(string, isUnique) {
const slug = slugify(string || 'anonymous', {
lower: true,
Expand Down
Loading