mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Implement GQL for groups
This commit is contained in:
parent
7e7bbe769e
commit
bf9dd205ca
@ -62,8 +62,9 @@ class Store {
|
||||
await txc.run('CALL apoc.schema.assert({},{},true)') // drop all indices
|
||||
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("user_fulltext_search",["Group"],["name", "slug", "description"])', // Wolle: check for 'name', 'slug', 'description'
|
||||
'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)),
|
||||
)
|
||||
|
||||
133
backend/src/models/Groups.js
Normal file
133
backend/src/models/Groups.js
Normal file
@ -0,0 +1,133 @@
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
export default {
|
||||
id: { type: 'string', primary: true, default: uuid }, // TODO: should be type: 'uuid' but simplified for our tests
|
||||
name: { type: 'string', disallow: [null], min: 3 },
|
||||
slug: { type: 'string', unique: 'true', regex: /^[a-z0-9_-]+$/, lowercase: true },
|
||||
avatar: {
|
||||
type: 'relationship',
|
||||
relationship: 'AVATAR_IMAGE',
|
||||
target: 'Image',
|
||||
direction: 'out',
|
||||
},
|
||||
deleted: { type: 'boolean', default: false },
|
||||
disabled: { type: 'boolean', default: false },
|
||||
wasSeeded: 'boolean', // Wolle: used or needed?
|
||||
locationName: { type: 'string', allow: [null] },
|
||||
about: { type: 'string', allow: [null, ''] }, // Wolle: null?
|
||||
description: { type: 'string', allow: [null, ''] }, // Wolle: null? HTML with Tiptap, similar to post content
|
||||
// Wolle: followedBy: {
|
||||
// type: 'relationship',
|
||||
// relationship: 'FOLLOWS',
|
||||
// target: 'User',
|
||||
// direction: 'in',
|
||||
// properties: {
|
||||
// createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
// },
|
||||
// },
|
||||
// Wolle: correct this way?
|
||||
members: { type: 'relationship',
|
||||
relationship: 'MEMBERS',
|
||||
target: 'User',
|
||||
direction: 'out'
|
||||
},
|
||||
// Wolle: needed? lastActiveAt: { type: 'string', isoDate: true },
|
||||
createdAt: {
|
||||
type: 'string',
|
||||
isoDate: true,
|
||||
default: () => new Date().toISOString()
|
||||
},
|
||||
updatedAt: {
|
||||
type: 'string',
|
||||
isoDate: true,
|
||||
required: true,
|
||||
default: () => new Date().toISOString(),
|
||||
},
|
||||
// Wolle: emoted: {
|
||||
// type: 'relationships',
|
||||
// relationship: 'EMOTED',
|
||||
// target: 'Post',
|
||||
// direction: 'out',
|
||||
// properties: {
|
||||
// emotion: {
|
||||
// type: 'string',
|
||||
// valid: ['happy', 'cry', 'surprised', 'angry', 'funny'],
|
||||
// invalid: [null],
|
||||
// },
|
||||
// },
|
||||
// eager: true,
|
||||
// cascade: true,
|
||||
// },
|
||||
// Wolle: blocked: {
|
||||
// type: 'relationship',
|
||||
// relationship: 'BLOCKED',
|
||||
// target: 'User',
|
||||
// direction: 'out',
|
||||
// properties: {
|
||||
// createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
// },
|
||||
// },
|
||||
// Wolle: muted: {
|
||||
// type: 'relationship',
|
||||
// relationship: 'MUTED',
|
||||
// target: 'User',
|
||||
// direction: 'out',
|
||||
// properties: {
|
||||
// createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
// },
|
||||
// },
|
||||
// Wolle: notifications: {
|
||||
// type: 'relationship',
|
||||
// relationship: 'NOTIFIED',
|
||||
// target: 'User',
|
||||
// direction: 'in',
|
||||
// },
|
||||
// Wolle inviteCodes: {
|
||||
// type: 'relationship',
|
||||
// relationship: 'GENERATED',
|
||||
// target: 'InviteCode',
|
||||
// direction: 'out',
|
||||
// },
|
||||
// Wolle: redeemedInviteCode: {
|
||||
// type: 'relationship',
|
||||
// relationship: 'REDEEMED',
|
||||
// target: 'InviteCode',
|
||||
// direction: 'out',
|
||||
// },
|
||||
// Wolle: shouted: {
|
||||
// type: 'relationship',
|
||||
// relationship: 'SHOUTED',
|
||||
// target: 'Post',
|
||||
// direction: 'out',
|
||||
// properties: {
|
||||
// createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
// },
|
||||
// },
|
||||
isIn: {
|
||||
type: 'relationship',
|
||||
relationship: 'IS_IN',
|
||||
target: 'Location',
|
||||
direction: 'out',
|
||||
},
|
||||
// Wolle: pinned: {
|
||||
// type: 'relationship',
|
||||
// relationship: 'PINNED',
|
||||
// target: 'Post',
|
||||
// direction: 'out',
|
||||
// properties: {
|
||||
// createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
// },
|
||||
// },
|
||||
// Wolle: showShoutsPublicly: {
|
||||
// type: 'boolean',
|
||||
// default: false,
|
||||
// },
|
||||
// Wolle: sendNotificationEmails: {
|
||||
// type: 'boolean',
|
||||
// default: true,
|
||||
// },
|
||||
// Wolle: locale: {
|
||||
// type: 'string',
|
||||
// allow: [null],
|
||||
// },
|
||||
}
|
||||
@ -4,6 +4,7 @@ export default {
|
||||
Image: require('./Image.js').default,
|
||||
Badge: require('./Badge.js').default,
|
||||
User: require('./User.js').default,
|
||||
Group: require('./Group.js').default,
|
||||
EmailAddress: require('./EmailAddress.js').default,
|
||||
UnverifiedEmailAddress: require('./UnverifiedEmailAddress.js').default,
|
||||
SocialMedia: require('./SocialMedia.js').default,
|
||||
|
||||
6
backend/src/schema/types/enum/GroupActionRadius.gql
Normal file
6
backend/src/schema/types/enum/GroupActionRadius.gql
Normal file
@ -0,0 +1,6 @@
|
||||
enum GroupActionRadius {
|
||||
regional
|
||||
national
|
||||
continental
|
||||
international
|
||||
}
|
||||
5
backend/src/schema/types/enum/GroupType.gql
Normal file
5
backend/src/schema/types/enum/GroupType.gql
Normal file
@ -0,0 +1,5 @@
|
||||
enum GroupType {
|
||||
public
|
||||
closed
|
||||
hidden
|
||||
}
|
||||
249
backend/src/schema/types/type/Group.gql
Normal file
249
backend/src/schema/types/type/Group.gql
Normal file
@ -0,0 +1,249 @@
|
||||
enum _GroupOrdering {
|
||||
id_asc
|
||||
id_desc
|
||||
name_asc
|
||||
name_desc
|
||||
slug_asc
|
||||
slug_desc
|
||||
locationName_asc
|
||||
locationName_desc
|
||||
about_asc
|
||||
about_desc
|
||||
createdAt_asc
|
||||
createdAt_desc
|
||||
updatedAt_asc
|
||||
updatedAt_desc
|
||||
# Wolle: needed? locale_asc
|
||||
# locale_desc
|
||||
}
|
||||
|
||||
type Group {
|
||||
id: ID!
|
||||
name: String # title
|
||||
slug: String!
|
||||
|
||||
createdAt: String
|
||||
updatedAt: String
|
||||
deleted: Boolean
|
||||
disabled: Boolean
|
||||
|
||||
avatar: Image @relation(name: "AVATAR_IMAGE", direction: "OUT")
|
||||
|
||||
location: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l")
|
||||
locationName: String
|
||||
about: String # goal
|
||||
description: String
|
||||
groupType: GroupType
|
||||
actionRadius: GroupActionRadius
|
||||
|
||||
categories: [Category] @relation(name: "CATEGORIZED", direction: "OUT")
|
||||
|
||||
# Wolle: needed?
|
||||
socialMedia: [SocialMedia]! @relation(name: "OWNED_BY", direction: "IN")
|
||||
|
||||
# Wolle: showShoutsPublicly: Boolean
|
||||
# Wolle: sendNotificationEmails: Boolean
|
||||
# Wolle: needed? locale: String
|
||||
members: [User]! @relation(name: "MEMBERS", direction: "OUT")
|
||||
membersCount: Int!
|
||||
@cypher(statement: "MATCH (this)-[:MEMBERS]->(r:User) RETURN COUNT(DISTINCT r)")
|
||||
|
||||
# Wolle: followedBy: [User]! @relation(name: "FOLLOWS", direction: "IN")
|
||||
# Wolle: followedByCount: Int!
|
||||
# @cypher(statement: "MATCH (this)<-[:FOLLOWS]-(r:User) RETURN COUNT(DISTINCT r)")
|
||||
|
||||
# Wolle: inviteCodes: [InviteCode] @relation(name: "GENERATED", direction: "OUT")
|
||||
# Wolle: redeemedInviteCode: InviteCode @relation(name: "REDEEMED", direction: "OUT")
|
||||
|
||||
# Is the currently logged in user following that user?
|
||||
# Wolle: followedByCurrentUser: Boolean!
|
||||
# @cypher(
|
||||
# statement: """
|
||||
# MATCH (this)<-[:FOLLOWS]-(u:User { id: $cypherParams.currentUserId})
|
||||
# RETURN COUNT(u) >= 1
|
||||
# """
|
||||
# )
|
||||
|
||||
# Wolle: isBlocked: Boolean!
|
||||
# @cypher(
|
||||
# statement: """
|
||||
# MATCH (this)<-[:BLOCKED]-(user:User {id: $cypherParams.currentUserId})
|
||||
# RETURN COUNT(user) >= 1
|
||||
# """
|
||||
# )
|
||||
# Wolle: blocked: Boolean!
|
||||
# @cypher(
|
||||
# statement: """
|
||||
# MATCH (this)-[:BLOCKED]-(user:User {id: $cypherParams.currentUserId})
|
||||
# RETURN COUNT(user) >= 1
|
||||
# """
|
||||
# )
|
||||
|
||||
# Wolle: isMuted: Boolean!
|
||||
# @cypher(
|
||||
# statement: """
|
||||
# MATCH (this)<-[:MUTED]-(user:User { id: $cypherParams.currentUserId})
|
||||
# RETURN COUNT(user) >= 1
|
||||
# """
|
||||
# )
|
||||
|
||||
# contributions: [WrittenPost]!
|
||||
# contributions2(first: Int = 10, offset: Int = 0): [WrittenPost2]!
|
||||
# @cypher(
|
||||
# statement: "MATCH (this)-[w:WROTE]->(p:Post) RETURN p as Post, w.timestamp as timestamp"
|
||||
# )
|
||||
# Wolle: needed?
|
||||
# contributions: [Post]! @relation(name: "WROTE", direction: "OUT")
|
||||
# contributionsCount: Int!
|
||||
# @cypher(
|
||||
# statement: """
|
||||
# MATCH (this)-[:WROTE]->(r:Post)
|
||||
# WHERE NOT r.deleted = true AND NOT r.disabled = true
|
||||
# RETURN COUNT(r)
|
||||
# """
|
||||
# )
|
||||
|
||||
# Wolle: comments: [Comment]! @relation(name: "WROTE", direction: "OUT")
|
||||
# commentedCount: Int!
|
||||
# @cypher(
|
||||
# statement: "MATCH (this)-[:WROTE]->(:Comment)-[:COMMENTS]->(p:Post) WHERE NOT p.deleted = true AND NOT p.disabled = true RETURN COUNT(DISTINCT(p))"
|
||||
# )
|
||||
|
||||
# Wolle: shouted: [Post]! @relation(name: "SHOUTED", direction: "OUT")
|
||||
# shoutedCount: Int!
|
||||
# @cypher(
|
||||
# statement: "MATCH (this)-[:SHOUTED]->(r:Post) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)"
|
||||
# )
|
||||
|
||||
# Wolle: badges: [Badge]! @relation(name: "REWARDED", direction: "IN")
|
||||
# badgesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)")
|
||||
|
||||
# Wolle: emotions: [EMOTED]
|
||||
}
|
||||
|
||||
|
||||
input _GroupFilter {
|
||||
AND: [_GroupFilter!]
|
||||
OR: [_GroupFilter!]
|
||||
name_contains: String
|
||||
about_contains: String
|
||||
description_contains: String
|
||||
slug_contains: String
|
||||
id: ID
|
||||
id_not: ID
|
||||
id_in: [ID!]
|
||||
id_not_in: [ID!]
|
||||
# Wolle:
|
||||
# friends: _GroupFilter
|
||||
# friends_not: _GroupFilter
|
||||
# friends_in: [_GroupFilter!]
|
||||
# friends_not_in: [_GroupFilter!]
|
||||
# friends_some: _GroupFilter
|
||||
# friends_none: _GroupFilter
|
||||
# friends_single: _GroupFilter
|
||||
# friends_every: _GroupFilter
|
||||
# following: _GroupFilter
|
||||
# following_not: _GroupFilter
|
||||
# following_in: [_GroupFilter!]
|
||||
# following_not_in: [_GroupFilter!]
|
||||
# following_some: _GroupFilter
|
||||
# following_none: _GroupFilter
|
||||
# following_single: _GroupFilter
|
||||
# following_every: _GroupFilter
|
||||
# followedBy: _GroupFilter
|
||||
# followedBy_not: _GroupFilter
|
||||
# followedBy_in: [_GroupFilter!]
|
||||
# followedBy_not_in: [_GroupFilter!]
|
||||
# followedBy_some: _GroupFilter
|
||||
# followedBy_none: _GroupFilter
|
||||
# followedBy_single: _GroupFilter
|
||||
# followedBy_every: _GroupFilter
|
||||
# role_in: [UserGroup!]
|
||||
}
|
||||
|
||||
type Query {
|
||||
Group(
|
||||
id: ID
|
||||
email: String # admins need to search for a user sometimes
|
||||
name: String
|
||||
slug: String
|
||||
locationName: String
|
||||
about: String
|
||||
description: String
|
||||
createdAt: String
|
||||
updatedAt: String
|
||||
first: Int
|
||||
offset: Int
|
||||
orderBy: [_GroupOrdering]
|
||||
filter: _GroupFilter
|
||||
): [Group]
|
||||
|
||||
availableGroupTypes: [GroupType]!
|
||||
|
||||
# Wolle:
|
||||
# availableRoles: [UserGroup]!
|
||||
# mutedUsers: [User]
|
||||
# blockedUsers: [User]
|
||||
# isLoggedIn: Boolean!
|
||||
# currentUser: User
|
||||
# findUsers(query: String!,limit: Int = 10, filter: _GroupFilter): [User]!
|
||||
# @cypher(
|
||||
# statement: """
|
||||
# CALL db.index.fulltext.queryNodes('user_fulltext_search', $query)
|
||||
# YIELD node as post, score
|
||||
# MATCH (user)
|
||||
# WHERE score >= 0.2
|
||||
# AND NOT user.deleted = true AND NOT user.disabled = true
|
||||
# RETURN user
|
||||
# LIMIT $limit
|
||||
# """
|
||||
# )
|
||||
}
|
||||
|
||||
# Wolle: enum Deletable {
|
||||
# Post
|
||||
# Comment
|
||||
# }
|
||||
|
||||
type Mutation {
|
||||
CreateGroup (
|
||||
id: ID!
|
||||
name: String
|
||||
email: String
|
||||
slug: String
|
||||
avatar: ImageInput
|
||||
locationName: String
|
||||
about: String
|
||||
description: String
|
||||
# Wolle: add group settings
|
||||
# Wolle:
|
||||
# showShoutsPublicly: Boolean
|
||||
# sendNotificationEmails: Boolean
|
||||
# locale: String
|
||||
): Group
|
||||
|
||||
UpdateUser (
|
||||
id: ID!
|
||||
name: String
|
||||
email: String
|
||||
slug: String
|
||||
avatar: ImageInput
|
||||
locationName: String
|
||||
about: String
|
||||
description: String
|
||||
# Wolle:
|
||||
# showShoutsPublicly: Boolean
|
||||
# sendNotificationEmails: Boolean
|
||||
# locale: String
|
||||
): Group
|
||||
|
||||
DeleteGroup(id: ID!): Group
|
||||
|
||||
# Wolle:
|
||||
# muteUser(id: ID!): User
|
||||
# unmuteUser(id: ID!): User
|
||||
# blockUser(id: ID!): User
|
||||
# unblockUser(id: ID!): User
|
||||
|
||||
# Wolle: switchUserRole(role: UserGroup!, id: ID!): User
|
||||
}
|
||||
@ -156,19 +156,19 @@ input _UserFilter {
|
||||
|
||||
type Query {
|
||||
User(
|
||||
id: ID
|
||||
email: String # admins need to search for a user sometimes
|
||||
name: String
|
||||
slug: String
|
||||
role: UserGroup
|
||||
locationName: String
|
||||
about: String
|
||||
createdAt: String
|
||||
updatedAt: String
|
||||
first: Int
|
||||
offset: Int
|
||||
orderBy: [_UserOrdering]
|
||||
filter: _UserFilter
|
||||
id: ID
|
||||
email: String # admins need to search for a user sometimes
|
||||
name: String
|
||||
slug: String
|
||||
role: UserGroup
|
||||
locationName: String
|
||||
about: String
|
||||
createdAt: String
|
||||
updatedAt: String
|
||||
first: Int
|
||||
offset: Int
|
||||
orderBy: [_UserOrdering]
|
||||
filter: _UserFilter
|
||||
): [User]
|
||||
|
||||
availableRoles: [UserGroup]!
|
||||
@ -197,19 +197,19 @@ enum Deletable {
|
||||
|
||||
type Mutation {
|
||||
UpdateUser (
|
||||
id: ID!
|
||||
name: String
|
||||
email: String
|
||||
slug: String
|
||||
avatar: ImageInput
|
||||
locationName: String
|
||||
about: String
|
||||
termsAndConditionsAgreedVersion: String
|
||||
termsAndConditionsAgreedAt: String
|
||||
allowEmbedIframes: Boolean
|
||||
showShoutsPublicly: Boolean
|
||||
sendNotificationEmails: Boolean
|
||||
locale: String
|
||||
id: ID!
|
||||
name: String
|
||||
email: String
|
||||
slug: String
|
||||
avatar: ImageInput
|
||||
locationName: String
|
||||
about: String
|
||||
termsAndConditionsAgreedVersion: String
|
||||
termsAndConditionsAgreedAt: String
|
||||
allowEmbedIframes: Boolean
|
||||
showShoutsPublicly: Boolean
|
||||
sendNotificationEmails: Boolean
|
||||
locale: String
|
||||
): User
|
||||
|
||||
DeleteUser(id: ID!, resource: [Deletable]): User
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user