mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-12 23:35:58 +00:00
refactor(backend): types for context + slug (#8486)
Also these changes saw merge conflicts in #8463 so let's get them merged already. Co-authored-by: mahula <lenzmath@posteo.de>
This commit is contained in:
parent
e4ae0dfe50
commit
fac818a3e4
@ -95,6 +95,7 @@
|
|||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/lodash": "^4.17.16",
|
"@types/lodash": "^4.17.16",
|
||||||
"@types/node": "^22.15.3",
|
"@types/node": "^22.15.3",
|
||||||
|
"@types/slug": "^5.0.9",
|
||||||
"@types/uuid": "~9.0.1",
|
"@types/uuid": "~9.0.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||||
"@typescript-eslint/parser": "^5.62.0",
|
"@typescript-eslint/parser": "^5.62.0",
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import CONFIG from '@config/index'
|
|||||||
import { CATEGORIES_MIN, CATEGORIES_MAX } from '@constants/categories'
|
import { CATEGORIES_MIN, CATEGORIES_MAX } from '@constants/categories'
|
||||||
import { DESCRIPTION_WITHOUT_HTML_LENGTH_MIN } from '@constants/groups'
|
import { DESCRIPTION_WITHOUT_HTML_LENGTH_MIN } from '@constants/groups'
|
||||||
import { removeHtmlTags } from '@middleware/helpers/cleanHtml'
|
import { removeHtmlTags } from '@middleware/helpers/cleanHtml'
|
||||||
|
import type { Context } from '@src/server'
|
||||||
|
|
||||||
import Resolver, {
|
import Resolver, {
|
||||||
removeUndefinedNullValuesFromObject,
|
removeUndefinedNullValuesFromObject,
|
||||||
@ -22,7 +23,7 @@ import { createOrUpdateLocations } from './users/location'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
Query: {
|
Query: {
|
||||||
Group: async (_object, params, context, _resolveInfo) => {
|
Group: async (_object, params, context: Context, _resolveInfo) => {
|
||||||
const { isMember, id, slug, first, offset } = params
|
const { isMember, id, slug, first, offset } = params
|
||||||
let pagination = ''
|
let pagination = ''
|
||||||
const orderBy = 'ORDER BY group.createdAt DESC'
|
const orderBy = 'ORDER BY group.createdAt DESC'
|
||||||
@ -75,10 +76,10 @@ export default {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(error)
|
throw new Error(error)
|
||||||
} finally {
|
} finally {
|
||||||
session.close()
|
await session.close()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
GroupMembers: async (_object, params, context, _resolveInfo) => {
|
GroupMembers: async (_object, params, context: Context, _resolveInfo) => {
|
||||||
const { id: groupId } = params
|
const { id: groupId } = params
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
const readTxResultPromise = session.readTransaction(async (txc) => {
|
const readTxResultPromise = session.readTransaction(async (txc) => {
|
||||||
@ -96,7 +97,7 @@ export default {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(error)
|
throw new Error(error)
|
||||||
} finally {
|
} finally {
|
||||||
session.close()
|
await session.close()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
GroupCount: async (_object, params, context, _resolveInfo) => {
|
GroupCount: async (_object, params, context, _resolveInfo) => {
|
||||||
@ -134,7 +135,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
CreateGroup: async (_parent, params, context, _resolveInfo) => {
|
CreateGroup: async (_parent, params, context: Context, _resolveInfo) => {
|
||||||
const { categoryIds } = params
|
const { categoryIds } = params
|
||||||
delete params.categoryIds
|
delete params.categoryIds
|
||||||
params.locationName = params.locationName === '' ? null : params.locationName
|
params.locationName = params.locationName === '' ? null : params.locationName
|
||||||
@ -182,7 +183,7 @@ export default {
|
|||||||
`,
|
`,
|
||||||
{ userId: context.user.id, categoryIds, params },
|
{ userId: context.user.id, categoryIds, params },
|
||||||
)
|
)
|
||||||
const [group] = await ownerCreateGroupTransactionResponse.records.map((record) =>
|
const [group] = ownerCreateGroupTransactionResponse.records.map((record) =>
|
||||||
record.get('group'),
|
record.get('group'),
|
||||||
)
|
)
|
||||||
return group
|
return group
|
||||||
@ -197,10 +198,10 @@ export default {
|
|||||||
throw new UserInputError('Group with this slug already exists!')
|
throw new UserInputError('Group with this slug already exists!')
|
||||||
throw new Error(error)
|
throw new Error(error)
|
||||||
} finally {
|
} finally {
|
||||||
session.close()
|
await session.close()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UpdateGroup: async (_parent, params, context, _resolveInfo) => {
|
UpdateGroup: async (_parent, params, context: Context, _resolveInfo) => {
|
||||||
const { categoryIds } = params
|
const { categoryIds } = params
|
||||||
delete params.categoryIds
|
delete params.categoryIds
|
||||||
const { id: groupId, avatar: avatarInput } = params
|
const { id: groupId, avatar: avatarInput } = params
|
||||||
@ -257,7 +258,7 @@ export default {
|
|||||||
categoryIds,
|
categoryIds,
|
||||||
params,
|
params,
|
||||||
})
|
})
|
||||||
const [group] = await transactionResponse.records.map((record) => record.get('group'))
|
const [group] = transactionResponse.records.map((record) => record.get('group'))
|
||||||
if (avatarInput) {
|
if (avatarInput) {
|
||||||
await mergeImage(group, 'AVATAR_IMAGE', avatarInput, { transaction })
|
await mergeImage(group, 'AVATAR_IMAGE', avatarInput, { transaction })
|
||||||
}
|
}
|
||||||
@ -273,10 +274,10 @@ export default {
|
|||||||
throw new UserInputError('Group with this slug already exists!')
|
throw new UserInputError('Group with this slug already exists!')
|
||||||
throw new Error(error)
|
throw new Error(error)
|
||||||
} finally {
|
} finally {
|
||||||
session.close()
|
await session.close()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
JoinGroup: async (_parent, params, context, _resolveInfo) => {
|
JoinGroup: async (_parent, params, context: Context, _resolveInfo) => {
|
||||||
const { groupId, userId } = params
|
const { groupId, userId } = params
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
||||||
@ -294,7 +295,7 @@ export default {
|
|||||||
RETURN member {.*, myRoleInGroup: membership.role}
|
RETURN member {.*, myRoleInGroup: membership.role}
|
||||||
`
|
`
|
||||||
const transactionResponse = await transaction.run(joinGroupCypher, { groupId, userId })
|
const transactionResponse = await transaction.run(joinGroupCypher, { groupId, userId })
|
||||||
const [member] = await transactionResponse.records.map((record) => record.get('member'))
|
const [member] = transactionResponse.records.map((record) => record.get('member'))
|
||||||
return member
|
return member
|
||||||
})
|
})
|
||||||
try {
|
try {
|
||||||
@ -302,10 +303,10 @@ export default {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(error)
|
throw new Error(error)
|
||||||
} finally {
|
} finally {
|
||||||
session.close()
|
await session.close()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
LeaveGroup: async (_parent, params, context, _resolveInfo) => {
|
LeaveGroup: async (_parent, params, context: Context, _resolveInfo) => {
|
||||||
const { groupId, userId } = params
|
const { groupId, userId } = params
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
try {
|
try {
|
||||||
@ -313,10 +314,10 @@ export default {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(error)
|
throw new Error(error)
|
||||||
} finally {
|
} finally {
|
||||||
session.close()
|
await session.close()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ChangeGroupMemberRole: async (_parent, params, context, _resolveInfo) => {
|
ChangeGroupMemberRole: async (_parent, params, context: Context, _resolveInfo) => {
|
||||||
const { groupId, userId, roleInGroup } = params
|
const { groupId, userId, roleInGroup } = params
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
||||||
@ -353,7 +354,7 @@ export default {
|
|||||||
userId,
|
userId,
|
||||||
roleInGroup,
|
roleInGroup,
|
||||||
})
|
})
|
||||||
const [member] = await transactionResponse.records.map((record) => record.get('member'))
|
const [member] = transactionResponse.records.map((record) => record.get('member'))
|
||||||
return member
|
return member
|
||||||
})
|
})
|
||||||
try {
|
try {
|
||||||
@ -361,10 +362,10 @@ export default {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(error)
|
throw new Error(error)
|
||||||
} finally {
|
} finally {
|
||||||
session.close()
|
await session.close()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
RemoveUserFromGroup: async (_parent, params, context, _resolveInfo) => {
|
RemoveUserFromGroup: async (_parent, params, context: Context, _resolveInfo) => {
|
||||||
const { groupId, userId } = params
|
const { groupId, userId } = params
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
try {
|
try {
|
||||||
@ -372,10 +373,10 @@ export default {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(error)
|
throw new Error(error)
|
||||||
} finally {
|
} finally {
|
||||||
session.close()
|
await session.close()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
muteGroup: async (_parent, params, context, _resolveInfo) => {
|
muteGroup: async (_parent, params, context: Context, _resolveInfo) => {
|
||||||
const { groupId } = params
|
const { groupId } = params
|
||||||
const userId = context.user.id
|
const userId = context.user.id
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
@ -393,7 +394,7 @@ export default {
|
|||||||
userId,
|
userId,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
const [group] = await transactionResponse.records.map((record) => record.get('group'))
|
const [group] = transactionResponse.records.map((record) => record.get('group'))
|
||||||
return group
|
return group
|
||||||
})
|
})
|
||||||
try {
|
try {
|
||||||
@ -401,10 +402,10 @@ export default {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(error)
|
throw new Error(error)
|
||||||
} finally {
|
} finally {
|
||||||
session.close()
|
await session.close()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
unmuteGroup: async (_parent, params, context, _resolveInfo) => {
|
unmuteGroup: async (_parent, params, context: Context, _resolveInfo) => {
|
||||||
const { groupId } = params
|
const { groupId } = params
|
||||||
const userId = context.user.id
|
const userId = context.user.id
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
@ -422,7 +423,7 @@ export default {
|
|||||||
userId,
|
userId,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
const [group] = await transactionResponse.records.map((record) => record.get('group'))
|
const [group] = transactionResponse.records.map((record) => record.get('group'))
|
||||||
return group
|
return group
|
||||||
})
|
})
|
||||||
try {
|
try {
|
||||||
@ -430,7 +431,7 @@ export default {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(error)
|
throw new Error(error)
|
||||||
} finally {
|
} finally {
|
||||||
session.close()
|
await session.close()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
|
import type { Context } from '@src/server'
|
||||||
|
|
||||||
import uniqueSlug from './slugify/uniqueSlug'
|
import uniqueSlug from './slugify/uniqueSlug'
|
||||||
|
|
||||||
const isUniqueFor = (context, type) => {
|
const isUniqueFor = (context: Context, type: string) => {
|
||||||
return async (slug) => {
|
return async (slug: string) => {
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
try {
|
try {
|
||||||
const existingSlug = await session.readTransaction((transaction) => {
|
const existingSlug = await session.readTransaction((transaction) => {
|
||||||
return transaction.run(
|
return transaction.run(
|
||||||
`
|
`
|
||||||
MATCH(p:${type} {slug: $slug })
|
MATCH(p:${type} {slug: $slug })
|
||||||
RETURN p.slug
|
RETURN p.slug
|
||||||
`,
|
`,
|
||||||
{ slug },
|
{ slug },
|
||||||
@ -20,26 +20,50 @@ const isUniqueFor = (context, type) => {
|
|||||||
})
|
})
|
||||||
return existingSlug.records.length === 0
|
return existingSlug.records.length === 0
|
||||||
} finally {
|
} finally {
|
||||||
session.close()
|
await session.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Mutation: {
|
Mutation: {
|
||||||
SignupVerification: async (resolve, root, args, context, info) => {
|
SignupVerification: async (
|
||||||
|
resolve,
|
||||||
|
root,
|
||||||
|
args: { slug: string; name: string },
|
||||||
|
context: Context,
|
||||||
|
info,
|
||||||
|
) => {
|
||||||
args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'User')))
|
args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'User')))
|
||||||
return resolve(root, args, context, info)
|
return resolve(root, args, context, info)
|
||||||
},
|
},
|
||||||
CreateGroup: async (resolve, root, args, context, info) => {
|
CreateGroup: async (
|
||||||
|
resolve,
|
||||||
|
root,
|
||||||
|
args: { slug: string; name: string },
|
||||||
|
context: Context,
|
||||||
|
info,
|
||||||
|
) => {
|
||||||
args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'Group')))
|
args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'Group')))
|
||||||
return resolve(root, args, context, info)
|
return resolve(root, args, context, info)
|
||||||
},
|
},
|
||||||
CreatePost: async (resolve, root, args, context, info) => {
|
CreatePost: async (
|
||||||
|
resolve,
|
||||||
|
root,
|
||||||
|
args: { slug: string; title: string },
|
||||||
|
context: Context,
|
||||||
|
info,
|
||||||
|
) => {
|
||||||
args.slug = args.slug || (await uniqueSlug(args.title, isUniqueFor(context, 'Post')))
|
args.slug = args.slug || (await uniqueSlug(args.title, isUniqueFor(context, 'Post')))
|
||||||
return resolve(root, args, context, info)
|
return resolve(root, args, context, info)
|
||||||
},
|
},
|
||||||
UpdatePost: async (resolve, root, args, context, info) => {
|
UpdatePost: async (
|
||||||
|
resolve,
|
||||||
|
root,
|
||||||
|
args: { slug: string; title: string },
|
||||||
|
context: Context,
|
||||||
|
info,
|
||||||
|
) => {
|
||||||
// TODO: is this absolutely correct? what happens if "args.title" is not defined? may it works accidentally, because "args.title" or "args.slug" is always send?
|
// TODO: is this absolutely correct? what happens if "args.title" is not defined? may it works accidentally, because "args.title" or "args.slug" is always send?
|
||||||
args.slug = args.slug || (await uniqueSlug(args.title, isUniqueFor(context, 'Post')))
|
args.slug = args.slug || (await uniqueSlug(args.title, isUniqueFor(context, 'Post')))
|
||||||
return resolve(root, args, context, info)
|
return resolve(root, args, context, info)
|
||||||
|
|||||||
@ -14,9 +14,11 @@ describe('uniqueSlug', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('slugify null string', async () => {
|
it('slugify null string', async () => {
|
||||||
const string = null
|
const nullString = null
|
||||||
const isUnique = jest.fn().mockResolvedValue(true)
|
const isUnique = jest.fn().mockResolvedValue(true)
|
||||||
await expect(uniqueSlug(string, isUnique)).resolves.toEqual('anonymous')
|
await expect(uniqueSlug(nullString as unknown as string, isUnique)).resolves.toEqual(
|
||||||
|
'anonymous',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Converts umlaut to a two letter equivalent', async () => {
|
it('Converts umlaut to a two letter equivalent', async () => {
|
||||||
|
|||||||
@ -1,18 +1,15 @@
|
|||||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
||||||
import slugify from 'slug'
|
import slugify from 'slug'
|
||||||
|
|
||||||
export default async function uniqueSlug(string, isUnique) {
|
type IsUnique = (slug: string) => Promise<boolean>
|
||||||
const slug = slugify(string || 'anonymous', {
|
export default async function uniqueSlug(str: string, isUnique: IsUnique) {
|
||||||
|
const slug = slugify(str || 'anonymous', {
|
||||||
lower: true,
|
lower: true,
|
||||||
multicharmap: { Ä: 'AE', ä: 'ae', Ö: 'OE', ö: 'oe', Ü: 'UE', ü: 'ue', ß: 'ss' },
|
multicharmap: { Ä: 'AE', ä: 'ae', Ö: 'OE', ö: 'oe', Ü: 'UE', ü: 'ue', ß: 'ss' },
|
||||||
})
|
})
|
||||||
if (await isUnique(slug)) return slug
|
if (await isUnique(slug)) return slug
|
||||||
|
|
||||||
let count = 0
|
let count = 0
|
||||||
let uniqueSlug
|
let uniqueSlug: string
|
||||||
do {
|
do {
|
||||||
count += 1
|
count += 1
|
||||||
uniqueSlug = `${slug}-${count}`
|
uniqueSlug = `${slug}-${count}`
|
||||||
|
|||||||
@ -102,3 +102,4 @@ const createServer = (options?) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default createServer
|
export default createServer
|
||||||
|
export type Context = Awaited<ReturnType<ReturnType<typeof getContext>>>
|
||||||
|
|||||||
@ -1533,6 +1533,11 @@
|
|||||||
"@types/express-serve-static-core" "*"
|
"@types/express-serve-static-core" "*"
|
||||||
"@types/mime" "*"
|
"@types/mime" "*"
|
||||||
|
|
||||||
|
"@types/slug@^5.0.9":
|
||||||
|
version "5.0.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/slug/-/slug-5.0.9.tgz#e5b213a9d7797d40d362ba85e2a7bbcd4df4ed40"
|
||||||
|
integrity sha512-6Yp8BSplP35Esa/wOG1wLNKiqXevpQTEF/RcL/NV6BBQaMmZh4YlDwCgrrFSoUE4xAGvnKd5c+lkQJmPrBAzfQ==
|
||||||
|
|
||||||
"@types/stack-utils@^2.0.0":
|
"@types/stack-utils@^2.0.0":
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
|
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user